1use std::collections::HashSet;
2use std::iter;
3
4use crate::types::ProtoPath;
5
6#[derive(Clone, Debug, Default)]
7pub(crate) struct PathSet {
8 paths: HashSet<String>,
9}
10
11impl PathSet {
12 pub(crate) fn contains(&self, full_path: &ProtoPath) -> bool {
13 let mut path_str = full_path.to_string();
14 if !path_str.starts_with(".") {
15 path_str = format!(".{}", path_str).to_string();
16 }
17 self.find_matching(&path_str).is_some()
18 }
19
20 pub(crate) fn insert(&mut self, path: impl std::fmt::Display) {
21 self.paths.insert(path.to_string());
22 }
23
24 fn find_matching(&self, full_path: &str) -> Option<String> {
25 sub_path_iter(full_path).find_map(|path| {
26 if self.paths.contains(path) {
27 Some(path.to_string())
28 } else {
29 None
30 }
31 })
32 }
33}
34
35fn sub_path_iter(full_path: &str) -> impl Iterator<Item = &str> {
36 iter::once(full_path)
38 .chain(suffixes(full_path))
39 .chain(prefixes(full_path))
40 .chain(iter::once("."))
41}
42
43fn prefixes(fq_path: &str) -> impl Iterator<Item = &str> {
44 iter::successors(Some(fq_path), |path| {
45 #[allow(unknown_lints, clippy::manual_split_once)]
46 path.rsplitn(2, '.').nth(1).filter(|path| !path.is_empty())
47 })
48 .skip(1)
49}
50
51fn suffixes(fq_path: &str) -> impl Iterator<Item = &str> {
52 iter::successors(Some(fq_path), |path| {
53 #[allow(unknown_lints, clippy::manual_split_once)]
54 path.splitn(2, '.').nth(1).filter(|path| !path.is_empty())
55 })
56 .skip(1)
57}
58
59#[cfg(test)]
60#[cfg_attr(coverage_nightly, coverage(off))]
61mod tests {
62 use crate::PathSet;
63 use crate::types::ProtoPath;
64
65 #[test]
66 fn test_path_set() {
67 let mut ps_a = PathSet::default();
68 ps_a.insert(".my_package.MessageA.field_a");
69
70 assert!(ps_a.contains(&ProtoPath::new(".my_package.MessageA.field_a")));
71 assert!(!ps_a.contains(&ProtoPath::new(".my_package.MessageA.field_b")));
72 assert!(!ps_a.contains(&ProtoPath::new(".my_package.MessageB.field_a")));
73 assert!(!ps_a.contains(&ProtoPath::new(".other_package.MessageA.field_a")));
74
75 let mut ps_b = PathSet::default();
76 ps_b.insert(".my_package.MessageA");
77
78 assert!(ps_b.contains(&ProtoPath::new(".my_package.MessageA.field_a")));
79 assert!(ps_b.contains(&ProtoPath::new(".my_package.MessageA.field_b")));
80 assert!(!ps_b.contains(&ProtoPath::new(".my_package.MessageB.field_a")));
81 assert!(!ps_b.contains(&ProtoPath::new(".other_package.MessageA.field_a")));
82
83 let mut ps_c = PathSet::default();
84 ps_c.insert(".my_package");
85
86 assert!(ps_c.contains(&ProtoPath::new(".my_package.MessageA.field_a")));
87 assert!(ps_c.contains(&ProtoPath::new(".my_package.MessageA.field_b")));
88 assert!(ps_c.contains(&ProtoPath::new(".my_package.MessageB.field_a")));
89 assert!(!ps_c.contains(&ProtoPath::new(".other_package.MessageA.field_a")));
90
91 let mut ps_d = PathSet::default();
92 ps_d.insert(".");
93
94 assert!(ps_d.contains(&ProtoPath::new(".my_package.MessageA.field_a")));
95 assert!(ps_d.contains(&ProtoPath::new(".my_package.MessageA.field_b")));
96 assert!(ps_d.contains(&ProtoPath::new(".my_package.MessageB.field_a")));
97 assert!(ps_d.contains(&ProtoPath::new(".other_package.MessageA.field_a")));
98 }
99}