stree 0.1.0

A directory hierarchy mapping format, similar to mtree
Documentation
use crate::{Result, Error, error};
use std::str::FromStr;
use std::fmt::{self, Display};

#[cfg(feature = "serde")]
use serde::{Serialize, Deserialize};

/// Information about a system user
#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "kebab-case"))]
pub struct User
{
    pub name: Option<String>,
    pub id: libc::uid_t
}

/// Information about a system group
#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "kebab-case"))]
pub struct Group
{
    pub name: Option<String>,
    pub id: libc::gid_t
}

impl User
{
    /// Construct a user given an optional name and uid
    pub fn new(name: Option<&str>, id: libc::uid_t) -> Self
    {
        let mut r = Self::default();
        if let Some(name) = name
        {
            r.name = Some(name.to_owned());
        }
        r.id = id;
        r
    }
}

impl Group
{
    /// Construct a group given an optional name and gid
    pub fn new(name: Option<&str>, id: libc::gid_t) -> Self
    {
        let mut r = Self::default();
        if let Some(name) = name
        {
            r.name = Some(name.to_owned());
        }
        r.id = id;
        r
    }
}

impl FromStr for User
{
    type Err = Error;
    fn from_str(s: &str) -> Result<Self>
    {
        let mut r = Self::default();
        match s.split_once(':')
        {
            Some(parts) =>
            {
                if parts.0.starts_with('<')
                && parts.0.ends_with('>')
                {
                    r.name = None
                }
                else
                {
                    r.name = Some(parts.0.to_string())
                }
                r.id = parts.1.parse::<libc::uid_t>()?;
            }
            None => error!("Expected a value separator in 'user' field, got: '{s}'")
        }
        Ok(r)
    }
}

impl FromStr for Group
{
    type Err = Error;
    fn from_str(s: &str) -> Result<Self>
    {
        let mut r = Self::default();
        match s.split_once(':')
        {
            Some(parts) =>
            {
                if parts.0.starts_with('<')
                && parts.0.ends_with('>')
                {
                    r.name = None
                }
                else
                {
                    r.name = Some(parts.0.to_string())
                }
                r.id = parts.1.parse::<libc::gid_t>()?;
            }
            None => error!("Expected a value separator in 'group' field, got: '{s}'")
        }
        Ok(r)
    }
}

impl Display for User
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
    {
        write!(f,
            "{}:{}",
            if let Some(name) = &self.name { &name } else { "<unknown>" },
            self.id
        )
    }
}

impl Display for Group
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
    {
        write!(f,
            "{}:{}",
            if let Some(name) = &self.name { &name } else { "<unknown>" },
            self.id
        )
    }
}