path_list.rs

  1use std::{
  2    path::{Path, PathBuf},
  3    sync::Arc,
  4};
  5
  6use util::paths::SanitizedPath;
  7
  8/// A list of absolute paths, in a specific order.
  9///
 10/// The paths are stored in lexicographic order, so that they can be compared to
 11/// other path lists without regard to the order of the paths.
 12#[derive(Default, PartialEq, Eq, Debug, Clone)]
 13pub struct PathList {
 14    paths: Arc<[PathBuf]>,
 15    order: Arc<[usize]>,
 16}
 17
 18#[derive(Debug)]
 19pub struct SerializedPathList {
 20    pub paths: String,
 21    pub order: String,
 22}
 23
 24impl PathList {
 25    pub fn new<P: AsRef<Path>>(paths: &[P]) -> Self {
 26        let mut indexed_paths: Vec<(usize, PathBuf)> = paths
 27            .iter()
 28            .enumerate()
 29            .map(|(ix, path)| (ix, SanitizedPath::from(path).into()))
 30            .collect();
 31        indexed_paths.sort_by(|(_, a), (_, b)| a.cmp(b));
 32        let order = indexed_paths.iter().map(|e| e.0).collect::<Vec<_>>().into();
 33        let paths = indexed_paths
 34            .into_iter()
 35            .map(|e| e.1)
 36            .collect::<Vec<_>>()
 37            .into();
 38        Self { order, paths }
 39    }
 40
 41    pub fn is_empty(&self) -> bool {
 42        self.paths.is_empty()
 43    }
 44
 45    pub fn paths(&self) -> &[PathBuf] {
 46        self.paths.as_ref()
 47    }
 48
 49    pub fn order(&self) -> &[usize] {
 50        self.order.as_ref()
 51    }
 52
 53    pub fn is_lexicographically_ordered(&self) -> bool {
 54        self.order.iter().enumerate().all(|(i, &j)| i == j)
 55    }
 56
 57    pub fn deserialize(serialized: &SerializedPathList) -> Self {
 58        let mut paths: Vec<PathBuf> = if serialized.paths.is_empty() {
 59            Vec::new()
 60        } else {
 61            serde_json::from_str::<Vec<PathBuf>>(&serialized.paths)
 62                .unwrap_or(Vec::new())
 63                .into_iter()
 64                .map(|s| SanitizedPath::from(s).into())
 65                .collect()
 66        };
 67
 68        let mut order: Vec<usize> = serialized
 69            .order
 70            .split(',')
 71            .filter_map(|s| s.parse().ok())
 72            .collect();
 73
 74        if !paths.is_sorted() || order.len() != paths.len() {
 75            order = (0..paths.len()).collect();
 76            paths.sort();
 77        }
 78
 79        Self {
 80            paths: paths.into(),
 81            order: order.into(),
 82        }
 83    }
 84
 85    pub fn serialize(&self) -> SerializedPathList {
 86        use std::fmt::Write as _;
 87
 88        let paths = serde_json::to_string(&self.paths).unwrap_or_default();
 89
 90        let mut order = String::new();
 91        for ix in self.order.iter() {
 92            if !order.is_empty() {
 93                order.push(',');
 94            }
 95            write!(&mut order, "{}", *ix).unwrap();
 96        }
 97        SerializedPathList { paths, order }
 98    }
 99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104
105    #[test]
106    fn test_path_list() {
107        let list1 = PathList::new(&["a/d", "a/c"]);
108        let list2 = PathList::new(&["a/c", "a/d"]);
109
110        assert_eq!(list1.paths(), list2.paths());
111        assert_ne!(list1, list2);
112        assert_eq!(list1.order(), &[1, 0]);
113        assert_eq!(list2.order(), &[0, 1]);
114
115        let list1_deserialized = PathList::deserialize(&list1.serialize());
116        assert_eq!(list1_deserialized, list1);
117
118        let list2_deserialized = PathList::deserialize(&list2.serialize());
119        assert_eq!(list2_deserialized, list2);
120    }
121}