use super::{metadata::get_metadata, ProcessedFiles};
use crate::{
api::{app::consts::*, Safe},
Error, Result, XorUrl,
};
use log::info;
use std::{collections::BTreeMap, fs, path::Path};
use walkdir::{DirEntry, WalkDir};
const MAX_RECURSIVE_DEPTH: usize = 10_000;
pub(crate) async fn upload_file_to_net(
safe: &mut Safe,
path: &Path,
dry_run: bool,
) -> Result<XorUrl> {
let data = fs::read(path).map_err(|err| {
Error::InvalidInput(format!("Failed to read file from local location: {}", err))
})?;
let mime_type = mime_guess::from_path(&path);
match safe
.files_store_public_blob(&data, mime_type.first_raw(), dry_run)
.await
{
Ok(xorurl) => Ok(xorurl),
Err(err) => {
if let Error::InvalidMediaType(_) = err {
safe.files_store_public_blob(&data, None, dry_run).await
} else {
Err(err)
}
}
}
}
pub(crate) fn normalise_path_separator(from: &str) -> String {
str::replace(&from, "\\", "/")
}
pub(crate) async fn file_system_dir_walk(
safe: &mut Safe,
location: &str,
recursive: bool,
follow_links: bool,
dry_run: bool,
) -> Result<ProcessedFiles> {
let file_path = Path::new(location);
info!("Reading files from {}", file_path.display());
let (metadata, _) = get_metadata(&file_path, follow_links)?;
if metadata.is_dir() || !recursive {
let max_depth = if recursive { MAX_RECURSIVE_DEPTH } else { 1 };
let mut processed_files = BTreeMap::new();
let children_to_process = WalkDir::new(file_path)
.follow_links(follow_links)
.into_iter()
.filter_entry(|e| valid_depth(e, max_depth))
.filter_map(|v| v.ok());
for (idx, child) in children_to_process.enumerate() {
let current_file_path = child.path();
let current_path_str = current_file_path.to_str().unwrap_or("").to_string();
info!("Processing {}...", current_path_str);
let normalised_path = normalise_path_separator(¤t_path_str);
let result = get_metadata(¤t_file_path, follow_links);
match result {
Ok((metadata, _)) => {
if metadata.file_type().is_dir() {
if idx == 0 && normalised_path.ends_with('/') {
continue;
}
if !recursive {
continue;
}
processed_files.insert(
normalised_path.clone(),
(CONTENT_ADDED_SIGN.to_string(), String::default()),
);
}
if metadata.file_type().is_symlink() {
processed_files.insert(
normalised_path.clone(),
(CONTENT_ADDED_SIGN.to_string(), String::default()),
);
}
if metadata.file_type().is_file() {
match upload_file_to_net(safe, ¤t_file_path, dry_run).await {
Ok(xorurl) => {
processed_files.insert(
normalised_path,
(CONTENT_ADDED_SIGN.to_string(), xorurl),
);
}
Err(err) => {
processed_files.insert(
normalised_path.clone(),
(CONTENT_ERROR_SIGN.to_string(), format!("<{}>", err)),
);
info!("Skipping file \"{}\". {}", normalised_path, err);
}
}
}
}
Err(err) => {
processed_files.insert(
normalised_path.clone(),
(CONTENT_ERROR_SIGN.to_string(), format!("<{}>", err)),
);
info!(
"Skipping file \"{}\" since no metadata could be read from local location: {:?}",
normalised_path, err);
}
}
}
Ok(processed_files)
} else {
Err(Error::InvalidInput(format!(
"'{}' is not a directory. The \"recursive\" arg is only supported for folders.",
location
)))
}
}
fn valid_depth(entry: &DirEntry, max_depth: usize) -> bool {
entry
.file_name()
.to_str()
.map(|_| entry.depth() <= max_depth)
.unwrap_or(false)
}
pub(crate) async fn file_system_single_file(
safe: &mut Safe,
location: &str,
dry_run: bool,
) -> Result<ProcessedFiles> {
let file_path = Path::new(location);
info!("Reading file {}", file_path.display());
let (metadata, _) = get_metadata(&file_path, true)?;
let mut processed_files = BTreeMap::new();
let normalised_path = normalise_path_separator(file_path.to_str().unwrap_or(""));
if metadata.is_dir() {
Err(Error::InvalidInput(format!(
"'{}' is a directory, only individual files can be added. Use files sync operation for uploading folders",
location
)))
} else {
match upload_file_to_net(safe, &file_path, dry_run).await {
Ok(xorurl) => {
processed_files.insert(normalised_path, (CONTENT_ADDED_SIGN.to_string(), xorurl));
}
Err(err) => {
processed_files.insert(
normalised_path.clone(),
(CONTENT_ERROR_SIGN.to_string(), format!("<{}>", err)),
);
info!("Skipping file \"{}\". {}", normalised_path, err);
}
};
Ok(processed_files)
}
}