plot/lib/logic/path/
paths_result.rs1use std::collections::HashSet;
2
3use crate::lib::logic::{
5 AnchoredPathsDatum,
6 PattEnvIndex,
7 PatternsToApply,
8 ScannedToApply,
9 TabColumn,
10 TabPathStructure,
11 TabSortBy,
12 TabSortOrder,
13 TabSpec,
14 TableData,
15 TableOutput,
16};
17
18pub trait MatchLabel {
23 fn label() -> &'static str;
24}
25
26#[derive(Debug, Clone)]
27pub struct Matched;
28#[derive(Debug, Clone)]
29pub struct Mismatched;
30
31impl MatchLabel for Matched {
32 fn label() -> &'static str { "✔" } }
34
35impl MatchLabel for Mismatched {
36 fn label() -> &'static str { "✖" } }
38
39#[derive(Debug, Clone)]
44pub struct Partition<L: MatchLabel> {
45 pub paths: Vec<String>,
46 pub label: &'static str,
47 pub entry: AnchoredPathsDatum,
48 pub _marker: std::marker::PhantomData<L>,
49}
50
51struct EnvIndex<'a> {
56 pub dirs: HashSet<&'a str>,
57 pub files: Vec<&'a str>,
58}
59
60impl<'a> PattEnvIndex for EnvIndex<'a> {
61 fn has_dir(&self, dir: &str) -> bool { self.dirs.contains(dir) }
62
63 fn has_file_with_prefix(&self, prefix: &str) -> bool {
64 let start = self.files.partition_point(|&f| f < prefix);
65 start < self.files.len() && self.files[start].starts_with(prefix)
66 }
67
68 fn any_file_in_dir(&self, dir: &str, check: &mut dyn FnMut(&str) -> bool) -> bool {
69 let start = self.files.partition_point(|&f| f < dir);
70 for &f in &self.files[start..] {
71 if !f.starts_with(dir) {
72 break;
73 }
74 if check(f) {
75 return true;
76 }
77 }
78 false
79 }
80}
81
82#[derive(Debug, Clone)]
87pub struct PartitioningResult {
88 pub scanner: ScannedToApply,
89 pub patterns: PatternsToApply,
90
91 pub m: Partition<Matched>,
92 pub x: Partition<Mismatched>,
93
94 pub spec: TabSpec,
96}
97
98impl PartitioningResult {
99 pub fn new(scanner: ScannedToApply, patterns: PatternsToApply) -> Self {
100 let env_index = EnvIndex {
101 dirs: scanner.dirs.iter().map(|n| n.str.as_str()).collect(),
102 files: scanner.files.iter().map(|n| n.str.as_str()).collect(),
103 };
104
105 let mut m_vec = Vec::new();
106 let mut x_vec = Vec::new();
107
108 let all_paths = scanner.files.iter().chain(scanner.dirs.iter());
109
110 for node in all_paths {
111 let p = node.str.as_str();
112 if patterns.is_match(p, &env_index) {
113 m_vec.push(node.str.clone());
114 } else {
115 x_vec.push(node.str.clone());
116 }
117 }
118
119 let entry = scanner.stat.relation.clone();
120
121 Self {
122 scanner,
123 patterns,
124 m: Partition {
125 paths: m_vec,
126 label: Matched::label(),
127 entry: entry.clone(),
128 _marker: std::marker::PhantomData,
129 },
130 x: Partition { paths: x_vec, label: Mismatched::label(), entry, _marker: std::marker::PhantomData },
131 spec: TabSpec::default(),
132 }
133 }
134
135 pub fn sort(mut self, by: TabSortBy, order: TabSortOrder, structure: TabPathStructure) -> Self {
140 self.spec = self.spec.sort(by, order, structure);
141 self
142 }
143
144 pub fn columns(mut self, cols: &[TabColumn]) -> Self {
145 self.spec = self.spec.columns(cols);
146 self
147 }
148
149 pub fn trim(mut self, size: usize, page: Option<usize>) -> Self {
150 self.spec = self.spec.trim(size, page);
151 self
152 }
153
154 pub fn build_matched(&self) -> TableOutput {
159 TableData::gather(&self.m)
160 .sort(self.spec.sort_by, self.spec.sort_order, self.spec.structure)
161 .into_output(&self.spec)
162 }
163
164 pub fn build_mismatched(&self) -> TableOutput {
165 TableData::gather(&self.x)
166 .sort(self.spec.sort_by, self.spec.sort_order, self.spec.structure)
167 .into_output(&self.spec)
168 }
169}