pub mod identify;
pub mod format;
pub mod render;
pub mod walk;
use std::ffi::OsString;
use std::io::Write;
use std::path::PathBuf;
use std::time::SystemTime;
use stuffr::Error;
use stuffr_cli::cli::LsArgs;
pub enum LsStatus {
Complete,
Degraded,
}
pub fn invoked_as(args: &[OsString]) -> &'static str {
match args
.iter()
.skip(1)
.find(|a| !a.as_encoded_bytes().starts_with(b"-"))
{
Some(a) if a == "files" => "files",
_ => "ls",
}
}
pub fn run(args: LsArgs, invoked_as: &str) -> stuffr::Result<LsStatus> {
let folder = match args.path.as_deref() {
None => Some(PathBuf::from(".")),
Some("-") => None,
Some(p) => match std::fs::metadata(p) {
Ok(md) if md.is_dir() => Some(PathBuf::from(p)),
Ok(_) => None,
Err(e) => {
return Err(Error::Usage(format!(
"cannot read {}: {e}",
stuffr::fmt_name(p)
)));
}
},
};
match folder {
Some(root) => {
if let Err(e) = std::fs::read_dir(&root) {
return Err(Error::Usage(format!(
"cannot read {}: {e}",
stuffr::fmt_name(&root.to_string_lossy())
)));
}
list_folder(args, &root)
}
None => {
if invoked_as == "files" {
return Err(Error::Usage(
"files lists a folder; use stuffr list for an archive".into(),
));
}
let folder_only = [
(args.recursive, "-r"),
(args.all, "-a"),
(args.by_name, "-n"),
];
if let Some((_, flag)) = folder_only.iter().find(|(given, _)| *given) {
return Err(Error::Usage(format!(
"{flag} applies to a folder, not a file"
)));
}
crate::run_list(
args.path.as_deref().unwrap_or("-"),
args.json,
args.max_ratio,
args.memory_limit,
args.strict_fidelity,
)?;
Ok(LsStatus::Complete)
}
}
}
fn list_folder(args: LsArgs, root: &std::path::Path) -> stuffr::Result<LsStatus> {
let archive_only = [
(args.max_ratio.is_some(), "--max-ratio"),
(args.memory_limit.is_some(), "--memory-limit"),
(args.strict_fidelity, "--strict-fidelity"),
];
if let Some((_, flag)) = archive_only.iter().find(|(given, _)| *given) {
return Err(Error::Usage(format!(
"{flag} applies to an archive, not a folder"
)));
}
let opts = walk::WalkOptions {
recursive: args.recursive,
all: args.all,
by_name: args.by_name,
};
let listing = walk::walk(stuffr::registry(), root, &opts);
let mut out = std::io::BufWriter::new(std::io::stdout().lock());
if args.json {
writeln!(out, "{}", render::render_json(&listing))?;
} else {
render::render_text(&listing, SystemTime::now(), &mut out)?;
}
out.flush()?;
drop(out);
for w in &listing.warnings {
eprintln!("stuffr: {w}");
}
Ok(if listing.warnings.is_empty() {
LsStatus::Complete
} else {
LsStatus::Degraded
})
}