diff --git a/sn_cli/src/bin/main.rs b/sn_cli/src/bin/main.rs index 67294df045..53da2958eb 100644 --- a/sn_cli/src/bin/main.rs +++ b/sn_cli/src/bin/main.rs @@ -142,9 +142,7 @@ async fn main() -> Result<()> { SubCmd::WatchOnlyWallet(cmds) => { wo_wallet_cmds(cmds, &client, &client_data_dir_path, should_verify_store).await? } - SubCmd::Files(cmds) => { - files_cmds(cmds, &client, &client_data_dir_path, should_verify_store).await? - } + SubCmd::Files(cmds) => files_cmds(cmds, &client, &client_data_dir_path).await?, SubCmd::Folders(cmds) => { folders_cmds(cmds, &client, &client_data_dir_path, should_verify_store).await? } diff --git a/sn_cli/src/bin/subcommands/files.rs b/sn_cli/src/bin/subcommands/files.rs index 81ffdef31e..f1742fc959 100644 --- a/sn_cli/src/bin/subcommands/files.rs +++ b/sn_cli/src/bin/subcommands/files.rs @@ -6,25 +6,15 @@ // KIND, either express or implied. Please review the Licences for the specific language governing // permissions and limitations relating to use of the SAFE Network Software. -use autonomi::{ - download_file, download_files, ChunkManager, Estimator, FilesUploader, UploadedFile, - UPLOADED_FILES, -}; +use autonomi::{download_file, ChunkManager, Estimator, UploadedFile, UPLOADED_FILES}; use clap::Parser; -use color_eyre::{ - eyre::{bail, eyre}, - Help, Result, -}; -use sn_client::{ - protocol::storage::{Chunk, ChunkAddress, RetryStrategy}, - UploadCfg, -}; +use color_eyre::{eyre::eyre, Result}; +use sn_client::protocol::storage::{Chunk, ChunkAddress, RetryStrategy}; use sn_client::{Client, FilesApi, BATCH_SIZE}; use std::{ ffi::OsString, path::{Path, PathBuf}, }; -use walkdir::WalkDir; use xor_name::XorName; #[derive(Parser, Debug)] @@ -37,26 +27,7 @@ pub enum FilesCmds { #[clap(long, name = "make_public", default_value = "false", short = 'p')] make_data_public: bool, }, - Upload { - /// The location of the file(s) to upload. - /// - /// Can be a file or a directory. - #[clap(name = "path", value_name = "PATH")] - file_path: PathBuf, - /// The batch_size to split chunks into parallel handling batches - /// during payment and upload processing. - #[clap(long, default_value_t = BATCH_SIZE, short='b')] - batch_size: usize, - /// Should the file be made accessible to all. (This is irreversible) - #[clap(long, name = "make_public", default_value = "false", short = 'p')] - make_data_public: bool, - /// Set the strategy to use on chunk upload failure. Does not modify the spend failure retry attempts yet. - /// - /// Choose a retry strategy based on effort level, from 'quick' (least effort), through 'balanced', - /// to 'persistent' (most effort). - #[clap(long, default_value_t = RetryStrategy::Balanced, short = 'r', help = "Sets the retry strategy on upload failure. Options: 'quick' for minimal effort, 'balanced' for moderate effort, or 'persistent' for maximum effort.")] - retry_strategy: RetryStrategy, - }, + Download { /// The name to apply to the downloaded file. /// @@ -64,14 +35,14 @@ pub enum FilesCmds { /// /// If neither are, all the files uploaded by the current user will be downloaded again. #[clap(name = "name")] - file_name: Option, + file_name: OsString, /// The hex address of a file. /// /// If the address argument is used, the name argument must also be supplied. /// /// If neither are, all the files uploaded by the current user will be downloaded again. #[clap(name = "address")] - file_addr: Option, + file_addr: String, /// Flagging whether to show the holders of the uploaded chunks. /// Default to be not showing. #[clap(long, name = "show_holders", default_value = "false")] @@ -88,12 +59,7 @@ pub enum FilesCmds { }, } -pub(crate) async fn files_cmds( - cmds: FilesCmds, - client: &Client, - root_dir: &Path, - verify_store: bool, -) -> Result<()> { +pub(crate) async fn files_cmds(cmds: FilesCmds, client: &Client, root_dir: &Path) -> Result<()> { match cmds { FilesCmds::Estimate { path, @@ -105,37 +71,7 @@ pub(crate) async fn files_cmds( .estimate_cost(path, make_data_public, root_dir) .await? } - FilesCmds::Upload { - file_path, - batch_size, - retry_strategy, - make_data_public, - } => { - let files_count = count_files_in_path_recursively(&file_path); - - if files_count == 0 { - if file_path.is_dir() { - bail!( - "The directory specified for upload is empty. \ - Please verify the provided path." - ); - } else { - bail!("The provided file path is invalid. Please verify the path."); - } - } - let upload_cfg = UploadCfg { - batch_size, - verify_store, - retry_strategy, - ..Default::default() - }; - let files_uploader = FilesUploader::new(client.clone(), root_dir.to_path_buf()) - .set_make_data_public(make_data_public) - .set_upload_cfg(upload_cfg) - .insert_path(&file_path); - let _summary = files_uploader.start_upload().await?; - } FilesCmds::Download { file_name, file_addr, @@ -143,120 +79,80 @@ pub(crate) async fn files_cmds( batch_size, retry_strategy, } => { - if (file_name.is_some() && file_addr.is_none()) - || (file_addr.is_some() && file_name.is_none()) - { - return Err( - eyre!("Both the name and address must be supplied if either are used") - .suggestion( - "Please run the command again in the form 'files upload
'", - ), - ); - } - let mut download_dir = root_dir.to_path_buf(); let mut download_file_name = file_name.clone(); - if let Some(file_name) = file_name { - // file_name may direct the downloaded data to: - // - // the current directory (just a filename) - // eg safe files download myfile.txt ADDRESS - // - // a directory relative to the current directory (relative filename) - // eg safe files download my/relative/path/myfile.txt ADDRESS - // - // a directory relative to root of the filesystem (absolute filename) - // eg safe files download /home/me/mydir/myfile.txt ADDRESS - let file_name_path = Path::new(&file_name); - if file_name_path.is_dir() { - return Err(eyre!("Cannot download file to path: {:?}", file_name)); - } - let file_name_dir = file_name_path.parent(); - if file_name_dir.is_none() { - // just a filename, use the current_dir - download_dir = std::env::current_dir().unwrap_or(root_dir.to_path_buf()); - } else if file_name_path.is_relative() { - // relative to the current directory. Make the relative path - // into an absolute path by joining it to current_dir - if let Some(relative_dir) = file_name_dir { - let current_dir = std::env::current_dir().unwrap_or(root_dir.to_path_buf()); - download_dir = current_dir.join(relative_dir); - if !download_dir.exists() { - return Err(eyre!("Directory does not exist: {:?}", download_dir)); - } - if let Some(path_file_name) = file_name_path.file_name() { - download_file_name = Some(OsString::from(path_file_name)); - } + // file_name may direct the downloaded data to: + // + // the current directory (just a filename) + // eg safe files download myfile.txt ADDRESS + // + // a directory relative to the current directory (relative filename) + // eg safe files download my/relative/path/myfile.txt ADDRESS + // + // a directory relative to root of the filesystem (absolute filename) + // eg safe files download /home/me/mydir/myfile.txt ADDRESS + let file_name_path = Path::new(&file_name); + if file_name_path.is_dir() { + return Err(eyre!("Cannot download file to path: {:?}", file_name)); + } + let file_name_dir = file_name_path.parent(); + if file_name_dir.is_none() { + // just a filename, use the current_dir + download_dir = std::env::current_dir().unwrap_or(root_dir.to_path_buf()); + } else if file_name_path.is_relative() { + // relative to the current directory. Make the relative path + // into an absolute path by joining it to current_dir + if let Some(relative_dir) = file_name_dir { + let current_dir = std::env::current_dir().unwrap_or(root_dir.to_path_buf()); + download_dir = current_dir.join(relative_dir); + if !download_dir.exists() { + return Err(eyre!("Directory does not exist: {:?}", download_dir)); + } + if let Some(path_file_name) = file_name_path.file_name() { + download_file_name = OsString::from(path_file_name); } - } else { - // absolute dir - download_dir = file_name_dir.unwrap_or(root_dir).to_path_buf(); } + } else { + // absolute dir + download_dir = file_name_dir.unwrap_or(root_dir).to_path_buf(); } - let files_api: FilesApi = FilesApi::new(client.clone(), download_dir.clone()); - match (download_file_name, file_addr) { - (Some(download_file_name), Some(address_provided)) => { - let bytes = - hex::decode(&address_provided).expect("Input address is not a hex string"); - let xor_name_provided = XorName( - bytes - .try_into() - .expect("Failed to parse XorName from hex string"), - ); - // try to read the data_map if it exists locally. - let uploaded_files_path = root_dir.join(UPLOADED_FILES); - let expected_data_map_location = uploaded_files_path.join(address_provided); - let local_data_map = { - if expected_data_map_location.exists() { - let uploaded_file_metadata = - UploadedFile::read(&expected_data_map_location)?; + let files_api: FilesApi = FilesApi::new(client.clone(), download_dir.clone()); - uploaded_file_metadata.data_map.map(|bytes| Chunk { - address: ChunkAddress::new(xor_name_provided), - value: bytes, - }) - } else { - None - } - }; + let address_provided = file_addr; + let bytes = hex::decode(&address_provided).expect("Input address is not a hex string"); + let xor_name_provided = XorName( + bytes + .try_into() + .expect("Failed to parse XorName from hex string"), + ); + // try to read the data_map if it exists locally. + let uploaded_files_path = root_dir.join(UPLOADED_FILES); + let expected_data_map_location = uploaded_files_path.join(address_provided); + let local_data_map = { + if expected_data_map_location.exists() { + let uploaded_file_metadata = UploadedFile::read(&expected_data_map_location)?; - download_file( - files_api, - xor_name_provided, - (download_file_name, local_data_map), - &download_dir, - show_holders, - batch_size, - retry_strategy, - ) - .await - } - _ => { - println!("Attempting to download all files uploaded by the current user..."); - download_files( - &files_api, - root_dir, - show_holders, - batch_size, - retry_strategy, - ) - .await? + uploaded_file_metadata.data_map.map(|bytes| Chunk { + address: ChunkAddress::new(xor_name_provided), + value: bytes, + }) + } else { + None } - } + }; + + download_file( + files_api, + xor_name_provided, + (download_file_name, local_data_map), + &download_dir, + show_holders, + batch_size, + retry_strategy, + ) + .await } } Ok(()) } - -fn count_files_in_path_recursively(file_path: &PathBuf) -> u32 { - let entries_iterator = WalkDir::new(file_path).into_iter().flatten(); - let mut count = 0; - - entries_iterator.for_each(|entry| { - if entry.file_type().is_file() { - count += 1; - } - }); - count -} diff --git a/sn_cli/src/files.rs b/sn_cli/src/files.rs index 66341f4865..05cf4a6e70 100644 --- a/sn_cli/src/files.rs +++ b/sn_cli/src/files.rs @@ -13,7 +13,7 @@ mod files_uploader; mod upload; pub use chunk_manager::ChunkManager; -pub use download::{download_file, download_files}; +pub use download::download_file; pub use estimate::Estimator; pub use files_uploader::{FilesUploadStatusNotifier, FilesUploadSummary, FilesUploader}; pub use upload::{UploadedFile, UPLOADED_FILES}; diff --git a/sn_cli/src/files/download.rs b/sn_cli/src/files/download.rs index 2d200d8dd4..e455566f49 100644 --- a/sn_cli/src/files/download.rs +++ b/sn_cli/src/files/download.rs @@ -6,86 +6,19 @@ // KIND, either express or implied. Please review the Licences for the specific language governing // permissions and limitations relating to use of the SAFE Network Software. -use super::{ - get_progress_bar, - upload::{UploadedFile, UPLOADED_FILES}, -}; +use super::get_progress_bar; -use std::collections::BTreeSet; use std::ffi::OsString; use std::path::Path; -use color_eyre::Result; use indicatif::ProgressBar; -use walkdir::WalkDir; use xor_name::XorName; use sn_client::{ protocol::storage::{Chunk, ChunkAddress, RetryStrategy}, FilesApi, FilesDownload, FilesDownloadEvent, }; -use tracing::{debug, error, info}; - -/// The default folder to download files to. -const DOWNLOAD_FOLDER: &str = "safe_files"; - -pub async fn download_files( - files_api: &FilesApi, - root_dir: &Path, - show_holders: bool, - batch_size: usize, - retry_strategy: RetryStrategy, -) -> Result<()> { - info!("Downloading with batch size of {}", batch_size); - let uploaded_files_path = root_dir.join(UPLOADED_FILES); - let download_path = dirs_next::download_dir() - .unwrap_or(root_dir.to_path_buf()) - .join(DOWNLOAD_FOLDER); - std::fs::create_dir_all(download_path.as_path())?; - - #[allow(clippy::mutable_key_type)] - let mut uploaded_files = BTreeSet::new(); - - for entry in WalkDir::new(uploaded_files_path.clone()) { - let entry = entry?; - let path = entry.path(); - if path.is_file() { - let hex_xorname = path - .file_name() - .expect("Uploaded file to have name") - .to_str() - .expect("Failed to convert path to string"); - let bytes = hex::decode(hex_xorname)?; - let xor_name_bytes: [u8; 32] = bytes - .try_into() - .expect("Failed to parse XorName from hex string"); - let xor_name = XorName(xor_name_bytes); - let address = ChunkAddress::new(xor_name); - - let uploaded_file_metadata = UploadedFile::read(path)?; - let datamap_chunk = uploaded_file_metadata.data_map.map(|bytes| Chunk { - address, - value: bytes, - }); - uploaded_files.insert((xor_name, (uploaded_file_metadata.filename, datamap_chunk))); - } - } - - for (xorname, file_data) in uploaded_files.into_iter() { - download_file( - files_api.clone(), - xorname, - file_data, - &download_path, - show_holders, - batch_size, - retry_strategy, - ) - .await; - } - - Ok(()) -} +use tracing::{debug, error}; pub async fn download_file( files_api: FilesApi, diff --git a/sn_cli/src/lib.rs b/sn_cli/src/lib.rs index 0a85ce69b3..e67c4f962e 100644 --- a/sn_cli/src/lib.rs +++ b/sn_cli/src/lib.rs @@ -11,6 +11,6 @@ mod files; pub use acc_packet::AccountPacket; pub use files::{ - download_file, download_files, ChunkManager, Estimator, FilesUploadStatusNotifier, - FilesUploadSummary, FilesUploader, UploadedFile, UPLOADED_FILES, + download_file, ChunkManager, Estimator, FilesUploadStatusNotifier, FilesUploadSummary, + FilesUploader, UploadedFile, UPLOADED_FILES, };