Skip to content

Commit

Permalink
Add option to set download destination path
Browse files Browse the repository at this point in the history
  • Loading branch information
prasmussen committed Jan 21, 2023
1 parent b4fec0e commit 2a3ada8
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 13 deletions.
78 changes: 66 additions & 12 deletions src/files/download.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,28 @@ pub struct Config {
pub file_id: String,
pub existing_file_action: ExistingFileAction,
pub download_directories: bool,
pub destination_root: Option<PathBuf>,
}

impl Config {
fn canonical_destination_root(&self) -> Result<PathBuf, Error> {
if let Some(path) = &self.destination_root {
if !path.exists() {
Err(Error::DestinationPathDoesNotExist(path.clone()))
} else if !path.is_dir() {
Err(Error::DestinationPathNotADirectory(path.clone()))
} else {
path.canonicalize()
.map_err(|err| Error::CanonicalizeDestinationPath(path.clone(), err))
}
} else {
let current_path = PathBuf::from(".");
let canonical_current_path = current_path
.canonicalize()
.map_err(|err| Error::CanonicalizeDestinationPath(current_path.clone(), err))?;
Ok(canonical_current_path)
}
}
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
Expand All @@ -36,14 +58,11 @@ pub async fn download(config: Config) -> Result<(), Error> {
.await
.map_err(Error::GetFile)?;

let file_name = file.name.clone().ok_or(Error::MissingFileName)?;
let file_path = PathBuf::from(&file_name);

err_if_file_exists(&file_path, config.existing_file_action)?;
err_if_file_exists(&file, &config)?;
err_if_directory(&file, &config)?;

if drive_file::is_directory(&file) {
download_directory(&hub, &file).await?;
download_directory(&hub, &file, &config).await?;
} else {
download_regular(&hub, &file, &config).await?;
}
Expand All @@ -57,20 +76,25 @@ pub async fn download_regular(
config: &Config,
) -> Result<(), Error> {
let file_name = file.name.clone().ok_or(Error::MissingFileName)?;
let file_path = PathBuf::from(&file_name);
let root_path = config.canonical_destination_root()?;
let abs_file_path = root_path.join(&file_name);

let body = download_file(&hub, &config.file_id)
.await
.map_err(Error::DownloadFile)?;

println!("Downloading {}", file_name);
save_body_to_file(body, &file_path, file.md5_checksum.clone()).await?;
save_body_to_file(body, &abs_file_path, file.md5_checksum.clone()).await?;
println!("Successfully downloaded {} ", file_name,);

Ok(())
}

pub async fn download_directory(hub: &Hub, file: &google_drive3::api::File) -> Result<(), Error> {
pub async fn download_directory(
hub: &Hub,
file: &google_drive3::api::File,
config: &Config,
) -> Result<(), Error> {
let tree = FileTreeDrive::from_file(&hub, &file)
.await
.map_err(Error::CreateFileTree)?;
Expand All @@ -84,19 +108,26 @@ pub async fn download_directory(hub: &Hub, file: &google_drive3::api::File) -> R
human_bytes(tree_info.total_file_size as f64)
);

let root_path = config.canonical_destination_root()?;

for folder in &tree.folders() {
let folder_path = folder.relative_path();
let abs_folder_path = root_path.join(&folder_path);

println!("Creating directory {}", folder_path.display());
fs::create_dir_all(&folder_path).map_err(|err| Error::CreateDirectory(folder_path, err))?;
fs::create_dir_all(&abs_folder_path)
.map_err(|err| Error::CreateDirectory(abs_folder_path, err))?;

for file in folder.files() {
let body = download_file(&hub, &file.drive_id)
.await
.map_err(Error::DownloadFile)?;

let file_path = file.relative_path();
let abs_file_path = root_path.join(&file_path);

println!("Downloading file '{}'", file_path.display());
save_body_to_file(body, &file_path, file.md5.clone()).await?;
save_body_to_file(body, &abs_file_path, file.md5.clone()).await?;
}
}

Expand Down Expand Up @@ -139,6 +170,9 @@ pub enum Error {
ReadChunk(hyper::Error),
WriteChunk(io::Error),
CreateFileTree(file_tree_drive::Error),
DestinationPathDoesNotExist(PathBuf),
DestinationPathNotADirectory(PathBuf),
CanonicalizeDestinationPath(PathBuf, io::Error),
}

impl error::Error for Error {}
Expand Down Expand Up @@ -180,6 +214,22 @@ impl Display for Error {
Error::ReadChunk(err) => write!(f, "Failed read from stream: {}", err),
Error::WriteChunk(err) => write!(f, "Failed write to file: {}", err),
Error::CreateFileTree(err) => write!(f, "Failed to create file tree: {}", err),
Error::DestinationPathDoesNotExist(path) => {
write!(f, "Destination path '{}' does not exist", path.display())
}
Error::DestinationPathNotADirectory(path) => {
write!(
f,
"Destination path '{}' is not a directory",
path.display()
)
}
Error::CanonicalizeDestinationPath(path, err) => write!(
f,
"Failed to canonicalize destination path '{}': {}",
path.display(),
err
),
}
}
}
Expand Down Expand Up @@ -209,8 +259,12 @@ async fn save_body_to_file(
fs::rename(&tmp_file_path, &file_path).map_err(Error::RenameFile)
}

fn err_if_file_exists(file_path: &PathBuf, action: ExistingFileAction) -> Result<(), Error> {
if file_path.exists() && action == ExistingFileAction::Abort {
fn err_if_file_exists(file: &google_drive3::api::File, config: &Config) -> Result<(), Error> {
let file_name = file.name.clone().ok_or(Error::MissingFileName)?;
let root_path = config.canonical_destination_root()?;
let file_path = root_path.join(&file_name);

if file_path.exists() && config.existing_file_action == ExistingFileAction::Abort {
Err(Error::FileExists(file_path.clone()))
} else {
Ok(())
Expand Down
8 changes: 7 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ enum FileCommand {
/// Download directories
#[arg(long)]
recursive: bool,

/// Path where the file/directory should be downloaded to
#[arg(long, value_name = "PATH")]
destination: Option<PathBuf>,
},

/// Upload file
Expand All @@ -137,7 +141,7 @@ enum FileCommand {
#[arg(long, value_name = "DIRECTORY_ID")]
parent: Option<Vec<String>>,

/// Upload directories
/// Upload directories. Note that this will always create a new directory on drive and will not update existing directories with the same name
#[arg(long)]
recursive: bool,

Expand Down Expand Up @@ -294,6 +298,7 @@ async fn main() {
file_id,
overwrite,
recursive,
destination,
} => {
let existing_file_action = if overwrite {
ExistingFileAction::Overwrite
Expand All @@ -305,6 +310,7 @@ async fn main() {
file_id,
existing_file_action,
download_directories: recursive,
destination_root: destination,
})
.await
.unwrap_or_else(handle_error)
Expand Down

0 comments on commit 2a3ada8

Please sign in to comment.