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}