libzetta 0.5.0

libzetta is a stable interface for programmatic administration of ZFS
Documentation
use crate::zfs::ValidationResult;
use std::path::Path;

pub trait PathExt {
    fn get_pool(&self) -> Option<String>;
    fn get_snapshot(&self) -> Option<String>;
    fn get_bookmark(&self) -> Option<String>;

    fn is_snapshot(&self) -> bool {
        self.get_snapshot().is_some()
    }
    fn is_bookmark(&self) -> bool {
        self.get_bookmark().is_some()
    }
    fn is_volume_or_dataset(&self) -> bool {
        !self.is_bookmark() && !self.is_snapshot()
    }

    fn is_valid(&self) -> bool {
        if let Ok(()) = self.validate() {
            true
        } else {
            false
        }
    }

    fn validate(&self) -> ValidationResult;
}

impl PathExt for Path {
    fn get_pool(&self) -> Option<String> {
        if self.has_root() || self.components().count() < 2 {
            return None;
        }
        if let Some(root) = self.iter().next() {
            Some(root.to_string_lossy().to_string())
        } else {
            None
        }
    }

    fn get_snapshot(&self) -> Option<String> {
        if let Some(last) = self.file_name() {
            let as_str = last.to_string_lossy();
            if as_str.contains('@') {
                return as_str.rsplit('@').next().map(String::from);
            }
        }
        None
    }

    fn get_bookmark(&self) -> Option<String> {
        if let Some(last) = self.file_name() {
            let as_str = last.to_string_lossy();
            if as_str.contains('#') {
                return as_str.rsplit('#').next().map(String::from);
            }
        }
        None
    }

    fn validate(&self) -> ValidationResult {
        crate::zfs::validators::validate_name(self)
    }
}

impl<P: AsRef<Path>> PathExt for P {
    fn get_pool(&self) -> Option<String> {
        self.as_ref().get_pool()
    }

    fn get_snapshot(&self) -> Option<String> {
        self.as_ref().get_snapshot()
    }

    fn get_bookmark(&self) -> Option<String> {
        self.as_ref().get_bookmark()
    }

    fn validate(&self) -> ValidationResult {
        self.as_ref().validate()
    }
}

#[cfg(test)]
mod test {
    use super::PathExt;
    use std::path::PathBuf;

    #[test]
    fn valid_dataset_no_bookmarks_or_snapshots() {
        let path = PathBuf::from("tank/usr/home");

        assert_eq!(Some(String::from("tank")), path.get_pool());
        assert!(!path.is_snapshot());
        assert!(!path.is_bookmark());
        assert_eq!(None, path.get_snapshot());
        assert_eq!(None, path.get_bookmark());
        assert!(path.is_volume_or_dataset());
        assert!(path.is_valid());
    }

    #[test]
    fn not_valid_just_dataset() {
        let path = PathBuf::from("/usr/home");
        assert_eq!(None, path.get_pool());
        assert!(!path.is_snapshot());
        assert!(!path.is_bookmark());
        assert_eq!(None, path.get_snapshot());
        assert_eq!(None, path.get_bookmark());
        assert!(path.is_volume_or_dataset());
        assert!(!path.is_valid());
    }

    #[test]
    fn valid_snapshot() {
        let path = PathBuf::from("tank/usr/home@snap");

        assert_eq!(Some(String::from("tank")), path.get_pool());
        assert!(path.is_snapshot());
        assert!(!path.is_bookmark());
        assert_eq!(Some(String::from("snap")), path.get_snapshot());
        assert_eq!(None, path.get_bookmark());
        assert!(!path.is_volume_or_dataset());
        assert!(path.is_valid());
    }
    #[test]
    fn valid_bookmark() {
        let path = PathBuf::from("tank/usr/home#bookmark");

        assert_eq!(Some(String::from("tank")), path.get_pool());
        assert!(!path.is_snapshot());
        assert!(path.is_bookmark());
        assert_eq!(None, path.get_snapshot());
        assert_eq!(Some(String::from("bookmark")), path.get_bookmark());
        assert!(!path.is_volume_or_dataset());
        assert!(path.is_valid());
    }

    #[test]
    fn at_in_wrong_place() {
        let path = PathBuf::from("tank/usr@wat/home");
        assert!(!path.is_snapshot());
    }

    #[test]
    fn pound_in_wrong_place() {
        let path = PathBuf::from("tank/usr#wat/home");
        assert!(!path.is_bookmark());
    }
}