opendal/raw/oio/list/
flat_list.rs1use crate::raw::*;
19use crate::*;
20
21pub struct FlatLister<A: Access, L> {
58 acc: A,
59
60 next_dir: Option<oio::Entry>,
61 active_lister: Vec<(Option<oio::Entry>, L)>,
62}
63
64unsafe impl<A: Access, L> Send for FlatLister<A, L> {}
68unsafe impl<A: Access, L> Sync for FlatLister<A, L> {}
72
73impl<A, L> FlatLister<A, L>
74where
75 A: Access,
76{
77 pub fn new(acc: A, path: &str) -> FlatLister<A, L> {
79 FlatLister {
80 acc,
81 next_dir: Some(oio::Entry::new(path, Metadata::new(EntryMode::DIR))),
82 active_lister: vec![],
83 }
84 }
85}
86
87impl<A, L> oio::List for FlatLister<A, L>
88where
89 A: Access<Lister = L>,
90 L: oio::List,
91{
92 async fn next(&mut self) -> Result<Option<oio::Entry>> {
93 loop {
94 if let Some(de) = self.next_dir.take() {
95 let (_, mut l) = self.acc.list(de.path(), OpList::new()).await?;
96 if let Some(v) = l.next().await? {
97 self.active_lister.push((Some(de.clone()), l));
98
99 if v.mode().is_dir() {
100 if v.path() != de.path() {
102 self.next_dir = Some(v);
103 continue;
104 }
105 } else {
106 return Ok(Some(v));
107 }
108 }
109 }
110
111 let (de, lister) = match self.active_lister.last_mut() {
112 Some((de, lister)) => (de, lister),
113 None => return Ok(None),
114 };
115
116 match lister.next().await? {
117 Some(v) if v.mode().is_dir() => {
118 if v.path() != de.as_ref().expect("de should not be none here").path() {
120 self.next_dir = Some(v);
121 continue;
122 }
123 }
124 Some(v) => return Ok(Some(v)),
125 None => match de.take() {
126 Some(de) => {
127 return Ok(Some(de));
128 }
129 None => {
130 let _ = self.active_lister.pop();
131 continue;
132 }
133 },
134 }
135 }
136 }
137}
138
139impl<A, P> oio::BlockingList for FlatLister<A, P>
140where
141 A: Access<BlockingLister = P>,
142 P: oio::BlockingList,
143{
144 fn next(&mut self) -> Result<Option<oio::Entry>> {
145 loop {
146 if let Some(de) = self.next_dir.take() {
147 let (_, mut l) = self.acc.blocking_list(de.path(), OpList::new())?;
148 if let Some(v) = l.next()? {
149 self.active_lister.push((Some(de.clone()), l));
150
151 if v.mode().is_dir() {
152 if v.path() != de.path() {
154 self.next_dir = Some(v);
155 continue;
156 }
157 } else {
158 return Ok(Some(v));
159 }
160 }
161 }
162
163 let (de, lister) = match self.active_lister.last_mut() {
164 Some((de, lister)) => (de, lister),
165 None => return Ok(None),
166 };
167
168 match lister.next()? {
169 Some(v) if v.mode().is_dir() => {
170 if v.path() != de.as_ref().expect("de should not be none here").path() {
171 self.next_dir = Some(v);
172 continue;
173 }
174 }
175 Some(v) => return Ok(Some(v)),
176 None => match de.take() {
177 Some(de) => {
178 return Ok(Some(de));
179 }
180 None => {
181 let _ = self.active_lister.pop();
182 continue;
183 }
184 },
185 }
186 }
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 use std::collections::HashMap;
193 use std::sync::Arc;
194 use std::vec;
195 use std::vec::IntoIter;
196
197 use log::debug;
198 use oio::BlockingList;
199
200 use super::*;
201
202 #[derive(Debug)]
203 struct MockService {
204 map: HashMap<&'static str, Vec<&'static str>>,
205 }
206
207 impl MockService {
208 fn new() -> Self {
209 let mut map = HashMap::default();
210 map.insert("x/", vec!["x/x/"]);
211 map.insert("x/x/", vec!["x/x/x/"]);
212 map.insert("x/x/x/", vec!["x/x/x/x"]);
213
214 Self { map }
215 }
216
217 fn get(&self, path: &str) -> MockLister {
218 let inner = self.map.get(path).expect("must have value").to_vec();
219
220 MockLister {
221 inner: inner.into_iter(),
222 }
223 }
224 }
225
226 impl Access for MockService {
227 type Reader = ();
228 type BlockingReader = ();
229 type Writer = ();
230 type BlockingWriter = ();
231 type Lister = ();
232 type BlockingLister = MockLister;
233 type Deleter = ();
234 type BlockingDeleter = ();
235
236 fn info(&self) -> Arc<AccessorInfo> {
237 let am = AccessorInfo::default();
238 am.update_full_capability(|mut cap| {
239 cap.list = true;
240 cap
241 });
242 am.into()
243 }
244
245 fn blocking_list(&self, path: &str, _: OpList) -> Result<(RpList, Self::BlockingLister)> {
246 debug!("visit path: {path}");
247 Ok((RpList::default(), self.get(path)))
248 }
249 }
250
251 struct MockLister {
252 inner: IntoIter<&'static str>,
253 }
254
255 impl BlockingList for MockLister {
256 fn next(&mut self) -> Result<Option<oio::Entry>> {
257 Ok(self.inner.next().map(|path| {
258 if path.ends_with('/') {
259 oio::Entry::new(path, Metadata::new(EntryMode::DIR))
260 } else {
261 oio::Entry::new(path, Metadata::new(EntryMode::FILE))
262 }
263 }))
264 }
265 }
266
267 #[test]
268 fn test_blocking_list() -> Result<()> {
269 let _ = tracing_subscriber::fmt().with_test_writer().try_init();
270
271 let acc = MockService::new();
272 let mut lister = FlatLister::new(acc, "x/");
273
274 let mut entries = Vec::default();
275
276 while let Some(e) = lister.next()? {
277 entries.push(e)
278 }
279
280 assert_eq!(
281 entries[0],
282 oio::Entry::new("x/x/x/x", Metadata::new(EntryMode::FILE))
283 );
284
285 Ok(())
286 }
287}