mirror of
https://github.com/TeFiLeDo/tree-owners.git
synced 2024-11-22 04:16:17 +01:00
redo output
This commit is contained in:
parent
a806fb4de2
commit
02166a07ef
28
src/id.rs
28
src/id.rs
@ -1,28 +0,0 @@
|
|||||||
use std::{collections::BTreeSet, fmt::Display};
|
|
||||||
|
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
/// Unique `uid`s and `gid`s.
|
|
||||||
#[derive(Debug, Default, Serialize)]
|
|
||||||
pub struct Ids {
|
|
||||||
pub users: BTreeSet<u32>,
|
|
||||||
pub groups: BTreeSet<u32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Ids {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
writeln!(f, "users:")?;
|
|
||||||
for user in &self.users {
|
|
||||||
writeln!(f, " {user}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
writeln!(f)?;
|
|
||||||
|
|
||||||
writeln!(f, "groups:")?;
|
|
||||||
for group in &self.groups {
|
|
||||||
writeln!(f, " {group}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
43
src/main.rs
43
src/main.rs
@ -1,31 +1,42 @@
|
|||||||
use std::{fs::read_dir, os::linux::fs::MetadataExt, path::Path};
|
use std::{fs::read_dir, os::linux::fs::MetadataExt, path::Path};
|
||||||
|
|
||||||
use anyhow::{ensure, Context, Result};
|
use anyhow::{anyhow, ensure, Context, Result};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
use crate::{cli::Args, id::Ids, name::Names, output::Output};
|
use crate::{cli::Args, summary::Summary};
|
||||||
|
|
||||||
mod cli;
|
mod cli;
|
||||||
mod id;
|
mod summary;
|
||||||
mod name;
|
|
||||||
mod output;
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
human_panic::setup_panic!();
|
human_panic::setup_panic!();
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
|
|
||||||
let mut ids = Ids::default();
|
let mut summary = Summary::default();
|
||||||
for root in args.roots {
|
for root in args.roots {
|
||||||
fs_entry(&root, &mut ids)?;
|
fs_entry(&root, &mut summary)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let output: Box<dyn Output> = match args.raw {
|
if !args.raw {
|
||||||
false => Box::new(Names::try_from(ids).context("failed to get names")?),
|
let (uf, gf) = summary.lookup_names();
|
||||||
true => Box::new(ids),
|
|
||||||
};
|
for (uid, e) in uf {
|
||||||
|
eprintln!(
|
||||||
|
"{:#}",
|
||||||
|
anyhow!(e).context(format!("failed to get name for user {uid}"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (gid, e) in gf {
|
||||||
|
eprintln!(
|
||||||
|
"{:#}",
|
||||||
|
anyhow!(e).context(format!("failed to get name for group {gid}"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
let output = match args.json {
|
let output = match args.json {
|
||||||
false => output.human_readable(),
|
false => summary.to_string(),
|
||||||
true => output.json().context("failed json serialization")?,
|
true => serde_json::to_string_pretty(&summary).context("json serialization failed")?,
|
||||||
};
|
};
|
||||||
println!("{output}");
|
println!("{output}");
|
||||||
|
|
||||||
@ -33,7 +44,7 @@ fn main() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Perform gid & uid gathering for a file, or a directory and its children.
|
/// Perform gid & uid gathering for a file, or a directory and its children.
|
||||||
fn fs_entry(entry: &Path, summary: &mut Ids) -> Result<()> {
|
fn fs_entry(entry: &Path, summary: &mut Summary) -> Result<()> {
|
||||||
let display = entry.display();
|
let display = entry.display();
|
||||||
ensure!(
|
ensure!(
|
||||||
entry.is_symlink() || entry.exists(),
|
entry.is_symlink() || entry.exists(),
|
||||||
@ -43,8 +54,8 @@ fn fs_entry(entry: &Path, summary: &mut Ids) -> Result<()> {
|
|||||||
let meta = entry
|
let meta = entry
|
||||||
.symlink_metadata()
|
.symlink_metadata()
|
||||||
.context(format!("failed to get metadata for {}", display))?;
|
.context(format!("failed to get metadata for {}", display))?;
|
||||||
summary.users.insert(meta.st_uid());
|
summary.add_user(meta.st_uid());
|
||||||
summary.groups.insert(meta.st_gid());
|
summary.add_group(meta.st_gid());
|
||||||
|
|
||||||
if entry.is_dir() {
|
if entry.is_dir() {
|
||||||
let children = read_dir(entry).context(format!("failed to read dir {}", display))?;
|
let children = read_dir(entry).context(format!("failed to read dir {}", display))?;
|
||||||
|
59
src/name.rs
59
src/name.rs
@ -1,59 +0,0 @@
|
|||||||
use std::{collections::BTreeSet, fmt::Display};
|
|
||||||
|
|
||||||
use anyhow::{anyhow, Error, Result};
|
|
||||||
use file_owner::{Group, Owner};
|
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
use crate::id::Ids;
|
|
||||||
|
|
||||||
/// Unique user and group names.
|
|
||||||
#[derive(Debug, Default, Serialize)]
|
|
||||||
pub struct Names {
|
|
||||||
pub users: BTreeSet<String>,
|
|
||||||
pub groups: BTreeSet<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Names {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
writeln!(f, "users:")?;
|
|
||||||
for user in &self.users {
|
|
||||||
writeln!(f, " {user}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
writeln!(f)?;
|
|
||||||
|
|
||||||
writeln!(f, "groups:")?;
|
|
||||||
for group in &self.groups {
|
|
||||||
writeln!(f, " {group}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<Ids> for Names {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn try_from(value: Ids) -> Result<Self> {
|
|
||||||
Ok(Self {
|
|
||||||
users: value
|
|
||||||
.users
|
|
||||||
.into_iter()
|
|
||||||
.map(|u| match Owner::from_uid(u).name() {
|
|
||||||
Ok(Some(name)) => Ok(name),
|
|
||||||
Ok(None) => Err(anyhow!("no name for user {u}")),
|
|
||||||
Err(_) => Err(anyhow!("failed to get name for user {u}")),
|
|
||||||
})
|
|
||||||
.collect::<Result<_>>()?,
|
|
||||||
groups: value
|
|
||||||
.groups
|
|
||||||
.into_iter()
|
|
||||||
.map(|g| match Group::from_gid(g).name() {
|
|
||||||
Ok(Some(name)) => Ok(name),
|
|
||||||
Ok(None) => Err(anyhow!("no name for group {g}")),
|
|
||||||
Err(_) => Err(anyhow!("failed to get name for group {g}")),
|
|
||||||
})
|
|
||||||
.collect::<Result<_>>()?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
/// Data that can be printed to the console.
|
|
||||||
pub trait Output {
|
|
||||||
/// A human readable representation of the data.
|
|
||||||
fn human_readable(&self) -> String;
|
|
||||||
/// A `json` representation of the data.
|
|
||||||
fn json(&self) -> Result<String, serde_json::Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Output for T
|
|
||||||
where
|
|
||||||
T: ToString + Serialize,
|
|
||||||
{
|
|
||||||
fn human_readable(&self) -> String {
|
|
||||||
self.to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn json(&self) -> Result<String, serde_json::Error> {
|
|
||||||
serde_json::to_string_pretty(&self)
|
|
||||||
}
|
|
||||||
}
|
|
78
src/summary.rs
Normal file
78
src/summary.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
use std::{collections::BTreeMap, fmt::Display};
|
||||||
|
|
||||||
|
use file_owner::{FileOwnerError, Group, Owner};
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
/// Lists of unique [User]s and [Group]s.
|
||||||
|
#[derive(Debug, Default, Serialize)]
|
||||||
|
pub struct Summary {
|
||||||
|
users: BTreeMap<u32, Option<String>>,
|
||||||
|
groups: BTreeMap<u32, Option<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Summary {
|
||||||
|
/// Add a new [User] to the [Summary].
|
||||||
|
pub fn add_user(&mut self, uid: u32) {
|
||||||
|
self.users.entry(uid).or_default();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a new [Group] to the [Summary].
|
||||||
|
pub fn add_group(&mut self, gid: u32) {
|
||||||
|
self.groups.entry(gid).or_default();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Look up the names of all users and groups.
|
||||||
|
pub fn lookup_names(&mut self) -> (Vec<(u32, FileOwnerError)>, Vec<(u32, FileOwnerError)>) {
|
||||||
|
let mut user_failures = vec![];
|
||||||
|
for (uid, name) in &mut self.users {
|
||||||
|
if name.is_some() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
match Owner::from_uid(*uid).name() {
|
||||||
|
Ok(n) => *name = n,
|
||||||
|
Err(e) => user_failures.push((*uid, e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut group_failures = vec![];
|
||||||
|
for (gid, name) in &mut self.groups {
|
||||||
|
if name.is_some() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
match Group::from_gid(*gid).name() {
|
||||||
|
Ok(n) => *name = n,
|
||||||
|
Err(e) => group_failures.push((*gid, e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(user_failures, group_failures)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Summary {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self { users, groups } = self;
|
||||||
|
|
||||||
|
writeln!(f, "users:")?;
|
||||||
|
for (uid, name) in users {
|
||||||
|
match name {
|
||||||
|
None => writeln!(f, " {uid}")?,
|
||||||
|
Some(name) => writeln!(f, " {name} ({uid})")?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(f)?;
|
||||||
|
|
||||||
|
writeln!(f, "groups:")?;
|
||||||
|
for (gid, name) in groups {
|
||||||
|
match name {
|
||||||
|
None => writeln!(f, " {gid}")?,
|
||||||
|
Some(name) => writeln!(f, " {name} ({gid})")?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user