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, err};
#[cfg(feature = "serde")]
use serde::{Serialize, Deserialize};
#[cfg(feature = "jiff")]
use jiff::Timestamp;
#[cfg(feature = "chrono")]
use chrono::{DateTime, Utc};


/// Represents a [`libc::timespec`], seconds and nanoseconds
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "kebab-case"))]
pub struct Time
{
    pub seconds: u64,
    pub nanos: u64
}

impl FromStr for Time
{
    type Err = Error;
    fn from_str(s: &str) -> Result<Self>
    {
        if let Some(parts) = s.split_once(':')
        {
            Ok(Self {
                seconds: parts.0.parse::<u64>().map_err(|e| err!(e))?,
                nanos: parts.1.parse::<u64>().map_err(|e| err!(e))?
            })
        }
        else
        {
            error!("expected two parts separated by ':', got '{s}'")
        }
    }
}

#[cfg(feature = "jiff")]
impl TryFrom<Time> for Timestamp
{
    type Error = jiff::Error;
    fn try_from(t: Time) -> Result<Timestamp, jiff::Error>
    {
        Timestamp::new(t.seconds as i64, t.nanos as i32)
    }
}
#[cfg(feature = "chrono")]
impl TryFrom<Time> for DateTime<Utc>
{
    type Error = std::io::Error;
    fn try_from(t: Time) -> std::io::Result<DateTime<Utc>>
    {
        DateTime::<Utc>::from_timestamp(t.seconds as i64, t.nanos as u32)
            .ok_or_else(|| std::io::Error::other(
                format!("Time {}.{} is out of range for chrono::DateTime", t.seconds, t.nanos)
            ))
    }
}

#[cfg(feature = "chrono")]

impl Time
{
    /// Attempts to convert `self` into an unzoned [`Timestamp`], consuming self
    /// 
    /// Returns `Ok(Timestamp)` on success, or `Err(self)` on failure
    #[cfg(feature = "jiff")]
    pub fn into_timestamp(self) -> std::result::Result<Timestamp, Self>
    {
        Timestamp::try_from(self).map_err(|_| self)
    }

    /// Attempt to convert to an unzoned [`Timestamp`]
    #[cfg(feature = "jiff")]
    pub fn to_timestamp(&self) -> std::result::Result<Timestamp, jiff::Error>
    {
        Timestamp::try_from(*self)
    }

    /// Attempt to convert `self` into a UTC [`DateTime`], consuming self
    /// 
    /// Returns `Ok(DateTime<Utc>)` on success, or `Err(self)` on failure
    #[cfg(feature = "chrono")]
    pub fn into_datetime(self) -> std::result::Result<DateTime<Utc>, Self>
    {
        DateTime::<Utc>::try_from(self).map_err(|_| self)
    }

    /// Attempt to convert `self` into a UTC [`DateTime`]
    #[cfg(feature = "chrono")]
    pub fn to_datetime(&self) -> std::io::Result<DateTime<Utc>>
    {
        DateTime::<Utc>::try_from(*self)
    }
}

impl Display for Time
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
    {
        write!(f, "{}:{}", self.seconds, self.nanos)
    }
}

#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct FileTime
{
    pub access: Time,
    pub change: Time,
    pub modify: Time,
    pub birth:  Time
}

impl From<libc::stat> for FileTime
{
    fn from(st: libc::stat) -> Self
    {
        Self
        {
            access: Time { seconds: st.st_atime as u64, nanos: st.st_atime_nsec as u64 },
            change: Time { seconds: st.st_ctime as u64, nanos: st.st_ctime_nsec as u64 },
            modify: Time { seconds: st.st_mtime as u64, nanos: st.st_mtime_nsec as u64 },
            birth: Default::default()
        }
    }
}