use anyhow::{Context, Result};
use glob::glob;
use std::path::{Path, PathBuf};
pub fn resolve_source_files(source: &Path, recursive: bool) -> Result<Vec<PathBuf>> {
let source_str = source.to_string_lossy();
if source_str.contains('*') || source_str.contains('?') || source_str.contains('[') {
let mut files = Vec::new();
for entry in
glob(&source_str).with_context(|| format!("Invalid glob pattern: {source_str}"))?
{
match entry {
Ok(path) if path.is_file() => files.push(path),
Ok(path) if path.is_dir() && recursive => {
files.extend(walk_directory(&path)?);
}
Ok(_) => {} Err(e) => tracing::warn!("Failed to read glob entry: {}", e),
}
}
Ok(files)
} else if source.is_file() {
Ok(vec![source.to_path_buf()])
} else if source.exists() && source.is_dir() {
if recursive {
walk_directory(source)
} else {
anyhow::bail!(
"Source is a directory. Use --recursive flag or a glob pattern like '{}/*' to upload files",
source_str
);
}
} else {
let mut files = Vec::new();
for path in glob(&source_str)
.unwrap_or_else(|_| glob::glob("").unwrap())
.flatten()
{
if path.is_file() {
files.push(path);
} else if path.is_dir() && recursive {
files.extend(walk_directory(&path)?);
}
}
if files.is_empty() {
anyhow::bail!("Source file does not exist: {:?}", source);
}
Ok(files)
}
}
pub fn walk_directory(dir: &Path) -> Result<Vec<PathBuf>> {
let mut files = Vec::new();
for entry in std::fs::read_dir(dir)? {
let entry = entry?;
let path = entry.path();
let metadata = entry.metadata()?;
if metadata.is_file() {
files.push(path);
} else if metadata.is_dir() {
files.extend(walk_directory(&path)?);
}
}
Ok(files)
}
pub fn format_bytes(bytes: u64) -> String {
const UNITS: &[&str] = &["B", "KB", "MB", "GB", "TB"];
let mut size = bytes as f64;
let mut unit_idx = 0;
while size >= 1024.0 && unit_idx < UNITS.len() - 1 {
size /= 1024.0;
unit_idx += 1;
}
if unit_idx == 0 {
format!("{} {}", size as u64, UNITS[unit_idx])
} else {
format!("{:.2} {}", size, UNITS[unit_idx])
}
}