mirror of
https://github.com/TeFiLeDo/tree-owners.git
synced 2024-11-14 16:46:15 +01:00
79 lines
2.1 KiB
Rust
79 lines
2.1 KiB
Rust
use std::{fs::read_dir, os::linux::fs::MetadataExt, path::Path};
|
|
|
|
use anyhow::{anyhow, Context, Error, Result};
|
|
use clap::Parser;
|
|
|
|
use crate::{cli::Args, summary::Summary};
|
|
|
|
mod cli;
|
|
mod summary;
|
|
|
|
fn main() -> Result<()> {
|
|
human_panic::setup_panic!();
|
|
let args = Args::parse();
|
|
|
|
let mut summary = Summary::default();
|
|
for root in args.roots {
|
|
fs_entry(&root, &mut summary);
|
|
}
|
|
|
|
if !args.raw {
|
|
let (uf, gf) = summary.lookup_names();
|
|
uf.into_iter()
|
|
.for_each(|(uid, e)| print_err(e, format!("failed to get name for user {uid}")));
|
|
gf.into_iter()
|
|
.for_each(|(gid, e)| print_err(e, format!("failed to get name for group {gid}")));
|
|
}
|
|
let output = match args.json {
|
|
false => summary.to_string(),
|
|
true => serde_json::to_string_pretty(&summary).context("json serialization failed")?,
|
|
};
|
|
println!("{output}");
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Perform gid & uid gathering for a file, or a directory and its children.
|
|
fn fs_entry(entry: &Path, summary: &mut Summary) {
|
|
let display = entry.display();
|
|
if !entry.exists() && !entry.is_symlink() {
|
|
print_root_err(format!("{display} doesn't exist"));
|
|
return;
|
|
}
|
|
|
|
match entry.symlink_metadata() {
|
|
Ok(meta) => {
|
|
summary.add_user(meta.st_uid());
|
|
summary.add_group(meta.st_gid());
|
|
}
|
|
Err(e) => {
|
|
print_err(e, format!("failed to get metadata for {display}"));
|
|
}
|
|
};
|
|
|
|
if entry.is_dir() {
|
|
let children = match read_dir(entry) {
|
|
Ok(children) => children,
|
|
Err(e) => {
|
|
print_err(e, format!("failed to read dir {display}"));
|
|
return;
|
|
}
|
|
};
|
|
|
|
for child in children {
|
|
match child {
|
|
Ok(child) => fs_entry(&child.path(), summary),
|
|
Err(e) => print_err(e, format!("invalid child for {display}")),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn print_root_err(message: String) {
|
|
eprintln!("{:#}", anyhow!("{message}"));
|
|
}
|
|
|
|
fn print_err(err: impl Into<Error>, message: String) {
|
|
eprintln!("{:#}", err.into().context(message));
|
|
}
|