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}