libzetta/zfs/
pathext.rs

1use crate::zfs::ValidationResult;
2use std::path::Path;
3
4pub trait PathExt {
5    fn get_pool(&self) -> Option<String>;
6    fn get_snapshot(&self) -> Option<String>;
7    fn get_bookmark(&self) -> Option<String>;
8
9    fn is_snapshot(&self) -> bool {
10        self.get_snapshot().is_some()
11    }
12    fn is_bookmark(&self) -> bool {
13        self.get_bookmark().is_some()
14    }
15    fn is_volume_or_dataset(&self) -> bool {
16        !self.is_bookmark() && !self.is_snapshot()
17    }
18
19    fn is_valid(&self) -> bool {
20        if let Ok(()) = self.validate() {
21            true
22        } else {
23            false
24        }
25    }
26
27    fn validate(&self) -> ValidationResult;
28}
29
30impl PathExt for Path {
31    fn get_pool(&self) -> Option<String> {
32        if self.has_root() || self.components().count() < 2 {
33            return None;
34        }
35        if let Some(root) = self.iter().next() {
36            Some(root.to_string_lossy().to_string())
37        } else {
38            None
39        }
40    }
41
42    fn get_snapshot(&self) -> Option<String> {
43        if let Some(last) = self.file_name() {
44            let as_str = last.to_string_lossy();
45            if as_str.contains('@') {
46                return as_str.rsplit('@').next().map(String::from);
47            }
48        }
49        None
50    }
51
52    fn get_bookmark(&self) -> Option<String> {
53        if let Some(last) = self.file_name() {
54            let as_str = last.to_string_lossy();
55            if as_str.contains('#') {
56                return as_str.rsplit('#').next().map(String::from);
57            }
58        }
59        None
60    }
61
62    fn validate(&self) -> ValidationResult {
63        crate::zfs::validators::validate_name(self)
64    }
65}
66
67impl<P: AsRef<Path>> PathExt for P {
68    fn get_pool(&self) -> Option<String> {
69        self.as_ref().get_pool()
70    }
71
72    fn get_snapshot(&self) -> Option<String> {
73        self.as_ref().get_snapshot()
74    }
75
76    fn get_bookmark(&self) -> Option<String> {
77        self.as_ref().get_bookmark()
78    }
79
80    fn validate(&self) -> ValidationResult {
81        self.as_ref().validate()
82    }
83}
84
85#[cfg(test)]
86mod test {
87    use super::PathExt;
88    use std::path::PathBuf;
89
90    #[test]
91    fn valid_dataset_no_bookmarks_or_snapshots() {
92        let path = PathBuf::from("tank/usr/home");
93
94        assert_eq!(Some(String::from("tank")), path.get_pool());
95        assert!(!path.is_snapshot());
96        assert!(!path.is_bookmark());
97        assert_eq!(None, path.get_snapshot());
98        assert_eq!(None, path.get_bookmark());
99        assert!(path.is_volume_or_dataset());
100        assert!(path.is_valid());
101    }
102
103    #[test]
104    fn not_valid_just_dataset() {
105        let path = PathBuf::from("/usr/home");
106        assert_eq!(None, path.get_pool());
107        assert!(!path.is_snapshot());
108        assert!(!path.is_bookmark());
109        assert_eq!(None, path.get_snapshot());
110        assert_eq!(None, path.get_bookmark());
111        assert!(path.is_volume_or_dataset());
112        assert!(!path.is_valid());
113    }
114
115    #[test]
116    fn valid_snapshot() {
117        let path = PathBuf::from("tank/usr/home@snap");
118
119        assert_eq!(Some(String::from("tank")), path.get_pool());
120        assert!(path.is_snapshot());
121        assert!(!path.is_bookmark());
122        assert_eq!(Some(String::from("snap")), path.get_snapshot());
123        assert_eq!(None, path.get_bookmark());
124        assert!(!path.is_volume_or_dataset());
125        assert!(path.is_valid());
126    }
127    #[test]
128    fn valid_bookmark() {
129        let path = PathBuf::from("tank/usr/home#bookmark");
130
131        assert_eq!(Some(String::from("tank")), path.get_pool());
132        assert!(!path.is_snapshot());
133        assert!(path.is_bookmark());
134        assert_eq!(None, path.get_snapshot());
135        assert_eq!(Some(String::from("bookmark")), path.get_bookmark());
136        assert!(!path.is_volume_or_dataset());
137        assert!(path.is_valid());
138    }
139
140    #[test]
141    fn at_in_wrong_place() {
142        let path = PathBuf::from("tank/usr@wat/home");
143        assert!(!path.is_snapshot());
144    }
145
146    #[test]
147    fn pound_in_wrong_place() {
148        let path = PathBuf::from("tank/usr#wat/home");
149        assert!(!path.is_bookmark());
150    }
151}