stree 0.1.0

A directory hierarchy mapping format, similar to mtree
Documentation
use std::fmt::{self, Display};
use std::str::FromStr;
use crate::{Result, Error, error};
#[cfg(feature = "serde")]
use serde::{Serialize, Deserialize};

/// Represents information related to a regular (or special for character/block) device.
/// 
/// Can be constructed from either just the full device identifier (found in [`st_dev`](libc::stat::st_dev)/[`st_rdev`](libc::stat::st_rdev)),
/// or given both the major and minor of a device (found in [`stx_dev_major`](libc::statx::stx_dev_major)/[`stx_dev_minor`](libc::statx::stx_dev_minor))
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Device
{
    id: u64,
    major: u64,
    minor: u64
}

impl Device
{
    /// Construct a device from just the raw combined device identifier
    pub const fn new(id: u64) -> Self
    {
        Self
        {
            major: dev_to_major(id),
            minor: dev_to_minor(id),
            id
        }
    }

    /// Get the raw combined device number [`libc::dev_t`] as returned by [`libc::stat`]/[`libc::lstat`]
    /// in the `st_dev` and `st_rdev` fields
    pub const fn id(&self) -> u64
    {
        self.id
    }

    /// Get the device major identifier
    /// 
    /// The major identifies the driver or class of the device
    pub const fn major(&self) -> u64
    {
        self.major
    }

    /// Get the device minor identifier
    /// 
    /// The minor identifies a specific device among those handled by the
    /// driver identified by [`major`](Self::major)
    pub const fn minor(&self) -> u64
    {
        self.minor
    }

    /// Construct a device from the major and minor identifiers
    /// 
    /// useful when using [`libc::statx`], which does not return
    /// the combined identifier
    pub const fn from_both(major: u64, minor: u64) -> Self
    {
        Self
        {
            major,
            minor,
            id: dev_from_major_minor(major, minor)
        }
    }

}

const fn dev_from_major_minor(major: u64, minor: u64) -> u64
{
    (major & 0xFFFFF000) << 32 |
    (major & 0xFFF) << 8 |
    (minor & 0xFFFFFF00) << 12 |
    (minor & 0xFF)
}

const fn dev_to_major(dev: u64) -> u64
{
    (dev >> 8) & 0xFFFF
}

const fn dev_to_minor(dev: u64) -> u64
{
    (dev & 0xFF) | ((dev >> 12) & 0xFFFFFF00)
}

impl Display for Device
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
    {
        write!(f, "{}:{}:{}", self.id, self.major, self.minor)
    }
}

impl FromStr for Device
{
    type Err = Error;
    fn from_str(s: &str) -> Result<Self>
    {
        let parts: Vec<&str> = s.splitn(2, ':').collect();
        if !parts.len() == 3
        {
            error!("expected two ':' separators, making 3 values total. Input has {} values", parts.len())
        }
        Ok(Self
        {
            id:    parts[0].parse::<u64>()?,
            major: parts[1].parse::<u64>()?,
            minor: parts[2].parse::<u64>()?
        })
    }
}