flams_math_archives/archive_iter.rs
1/*
2use std::{
3 collections::VecDeque,
4 fs::ReadDir,
5 path::{Path, PathBuf},
6};
7
8pub struct ManifestIterator {
9 stack: VecDeque<(PathBuf, ReadDir)>,
10 curr: Option<ReadDir>,
11 //curr_path: PathBuf,
12}
13impl ManifestIterator {
14 #[must_use]
15 pub fn new(path: &Path) -> Self {
16 Self {
17 stack: VecDeque::new(),
18 curr: path.read_dir().ok(),
19 }
20 }
21}
22impl Iterator for ManifestIterator {
23 type Item = PathBuf;
24 fn next(&mut self) -> Option<Self::Item> {
25 loop {
26 let entry = match self.curr.as_mut().and_then(ReadDir::next) {
27 None => {
28 if let Some((_, next)) = self.stack.pop_front() {
29 self.curr = Some(next);
30 //self.curr_path = path;
31 continue;
32 }
33 return None;
34 }
35 Some(Ok(d)) => d,
36 _ => continue,
37 };
38 let Ok(md) = entry.metadata() else { continue };
39 if !md.is_dir() {
40 continue;
41 }
42 let file_name = entry.file_name();
43 let Some(file_name) = file_name.to_str() else {
44 continue;
45 };
46 if file_name.starts_with('.') {
47 continue;
48 }
49 let path = entry.path();
50 if file_name.eq_ignore_ascii_case("meta-inf")
51 && let Some(manifest) = find_manifest(&path)
52 {
53 // SAFETY: path is a child of self.curr
54 let parent = unsafe { path.parent().unwrap_unchecked() };
55 self.stack.retain(|(p, _)| !p.starts_with(parent));
56 self.curr = None;
57 return Some(manifest);
58 }
59 if let Ok(next) = path.read_dir() {
60 self.stack.push_back((path, next));
61 }
62 }
63 }
64}
65 */
66
67use std::path::{Path, PathBuf};
68
69pub(super) struct ManifestIterator {
70 stack: Vec<Vec<PathBuf>>,
71 curr: Option<std::fs::ReadDir>,
72}
73
74impl ManifestIterator {
75 pub fn new(path: &Path) -> Self {
76 Self {
77 stack: vec![vec![]],
78 curr: std::fs::read_dir(path)
79 .map_err(|_| {
80 tracing::warn!("Could not read directory {}", path.display());
81 })
82 .ok(),
83 }
84 }
85
86 fn next(curr: &mut Option<std::fs::ReadDir>, stack: &mut Vec<Vec<PathBuf>>) -> Option<PathBuf> {
87 loop {
88 let d = match curr.as_mut().and_then(std::fs::ReadDir::next) {
89 None => {
90 if Self::next_dir(stack, curr) {
91 continue;
92 }
93 return None;
94 }
95 Some(Ok(d)) => d,
96 _ => continue,
97 };
98 let Ok(md) = d.metadata() else { continue };
99 let path = d.path();
100
101 //let _span = tracing::debug_span!(target:"archives","checking","{}",path.display()).entered();
102 if md.is_dir() {
103 if d.file_name().to_str().is_none_or(|s| s.starts_with('.')) {
104 continue;
105 } else if d.file_name().eq_ignore_ascii_case("meta-inf")
106 && let Some(path) = find_manifest(&path)
107 {
108 stack.pop();
109 if !Self::next_dir(stack, curr) {
110 *curr = None;
111 }
112 return Some(path);
113 }
114 stack
115 .last_mut()
116 .unwrap_or_else(|| unreachable!())
117 .push(path);
118 }
119 }
120 }
121
122 fn next_dir(stack: &mut Vec<Vec<PathBuf>>, curr: &mut Option<std::fs::ReadDir>) -> bool {
123 loop {
124 match stack.last_mut() {
125 None => return false,
126 Some(s) => match s.pop() {
127 Some(e) => {
128 *curr = if let Ok(rd) = e.read_dir() {
129 Some(rd)
130 } else {
131 tracing::warn!(target:"archives","Could not read directory {}", e.display());
132 return false;
133 };
134 stack.push(Vec::new());
135 return true;
136 }
137 None => {
138 stack.pop();
139 }
140 },
141 }
142 }
143 }
144}
145
146impl Iterator for ManifestIterator {
147 type Item = PathBuf;
148 #[inline]
149 fn next(&mut self) -> Option<Self::Item> {
150 Self::next(&mut self.curr, &mut self.stack)
151 }
152}
153
154impl spliter::Spliterator for ManifestIterator {
155 fn split(&mut self) -> Option<Self> {
156 if self.stack.len() < 2 || self.stack[0].len() < 2 {
157 return None;
158 }
159 let stacksplit = self.stack[0].len() / 2;
160 let mut rightstack = self.stack[0].split_off(stacksplit);
161 std::mem::swap(&mut self.stack[0], &mut rightstack);
162 loop {
163 match rightstack.pop() {
164 None => return None,
165 Some(e) => {
166 if let Ok(rd) = std::fs::read_dir(&e) {
167 return Some(Self {
168 curr: Some(rd),
169 stack: vec![rightstack, Vec::new()],
170 });
171 }
172 }
173 }
174 }
175 }
176}
177
178pub(crate) fn find_manifest(metainf: &Path) -> Option<PathBuf> {
179 tracing::trace!("Checking manifest {}", metainf.display());
180 if let Ok(rd) = metainf.read_dir() {
181 for d in rd {
182 let Ok(manifest) = d else {
183 tracing::warn!("Could not read directory {}", metainf.display());
184 continue;
185 };
186 if !manifest.file_name().eq_ignore_ascii_case("manifest.mf") {
187 continue;
188 }
189 let path = manifest.path();
190 if !path.is_file() {
191 continue;
192 }
193 return Some(path);
194 }
195 tracing::trace!("not found");
196 } else {
197 tracing::warn!("Could not read directory {}", metainf.display());
198 }
199 None
200}