mirror of
				https://github.com/TeFiLeDo/tree-owners.git
				synced 2025-11-04 05:51:19 +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 anyhow::{ensure, Context, Result};
 | 
			
		||||
use anyhow::{anyhow, ensure, Context, Result};
 | 
			
		||||
use clap::Parser;
 | 
			
		||||
 | 
			
		||||
use crate::{cli::Args, id::Ids, name::Names, output::Output};
 | 
			
		||||
use crate::{cli::Args, summary::Summary};
 | 
			
		||||
 | 
			
		||||
mod cli;
 | 
			
		||||
mod id;
 | 
			
		||||
mod name;
 | 
			
		||||
mod output;
 | 
			
		||||
mod summary;
 | 
			
		||||
 | 
			
		||||
fn main() -> Result<()> {
 | 
			
		||||
    human_panic::setup_panic!();
 | 
			
		||||
    let args = Args::parse();
 | 
			
		||||
 | 
			
		||||
    let mut ids = Ids::default();
 | 
			
		||||
    let mut summary = Summary::default();
 | 
			
		||||
    for root in args.roots {
 | 
			
		||||
        fs_entry(&root, &mut ids)?;
 | 
			
		||||
        fs_entry(&root, &mut summary)?;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let output: Box<dyn Output> = match args.raw {
 | 
			
		||||
        false => Box::new(Names::try_from(ids).context("failed to get names")?),
 | 
			
		||||
        true => Box::new(ids),
 | 
			
		||||
    };
 | 
			
		||||
    if !args.raw {
 | 
			
		||||
        let (uf, gf) = summary.lookup_names();
 | 
			
		||||
 | 
			
		||||
        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 {
 | 
			
		||||
        false => output.human_readable(),
 | 
			
		||||
        true => output.json().context("failed json serialization")?,
 | 
			
		||||
        false => summary.to_string(),
 | 
			
		||||
        true => serde_json::to_string_pretty(&summary).context("json serialization failed")?,
 | 
			
		||||
    };
 | 
			
		||||
    println!("{output}");
 | 
			
		||||
 | 
			
		||||
@ -33,7 +44,7 @@ fn main() -> Result<()> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// 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();
 | 
			
		||||
    ensure!(
 | 
			
		||||
        entry.is_symlink() || entry.exists(),
 | 
			
		||||
@ -43,8 +54,8 @@ fn fs_entry(entry: &Path, summary: &mut Ids) -> Result<()> {
 | 
			
		||||
    let meta = entry
 | 
			
		||||
        .symlink_metadata()
 | 
			
		||||
        .context(format!("failed to get metadata for {}", display))?;
 | 
			
		||||
    summary.users.insert(meta.st_uid());
 | 
			
		||||
    summary.groups.insert(meta.st_gid());
 | 
			
		||||
    summary.add_user(meta.st_uid());
 | 
			
		||||
    summary.add_group(meta.st_gid());
 | 
			
		||||
 | 
			
		||||
    if entry.is_dir() {
 | 
			
		||||
        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