1mod filter;
126
127pub use crate::filter::{Accept, Closure, Extension, Filter, MaxDepth, NoSymlink, This};
128#[cfg(not(feature = "tokio"))]
129use std::fs::{read_dir, ReadDir};
130use std::num::NonZeroUsize;
131use std::path::{Path, PathBuf};
132#[cfg(feature = "tokio")]
133use tokio::fs::{read_dir, ReadDir};
134
135#[cfg(not(feature = "tokio"))]
140pub fn recursive_read_dir<P: AsRef<Path>>(
141 dir: P,
142) -> std::io::Result<impl Iterator<Item = PathBuf>> {
143 RecursiveDirIterator::from_root(dir)
144}
145
146#[cfg(feature = "tokio")]
151pub async fn recursive_read_dir<P: AsRef<Path>>(dir: P) -> std::io::Result<RecursiveDirIterator> {
152 RecursiveDirIterator::from_root(dir).await
153}
154
155pub struct RecursiveDirIterator<F = Accept> {
157 base_dir: PathBuf,
158 current_iter: Option<ReadDir>,
159 dirs: Vec<PathBuf>,
160 visit_filter: F,
161}
162
163impl RecursiveDirIterator {
164 #[cfg(not(feature = "tokio"))]
168 pub fn from_root(path: impl AsRef<Path>) -> std::io::Result<Self> {
169 let base_dir = path.as_ref().to_path_buf();
170 let r = read_dir(path)?;
171
172 Ok(RecursiveDirIterator {
173 base_dir,
174 current_iter: Some(r),
175 dirs: vec![],
176 visit_filter: Accept,
177 })
178 }
179
180 #[cfg(feature = "tokio")]
184 pub async fn from_root(path: impl AsRef<Path>) -> std::io::Result<Self> {
185 let base_dir = path.as_ref().to_path_buf();
186 let r = read_dir(path).await?;
187
188 Ok(RecursiveDirIterator {
189 base_dir,
190 current_iter: Some(r),
191 dirs: vec![],
192 visit_filter: Accept,
193 })
194 }
195}
196
197impl RecursiveDirIterator<NoSymlink> {
198 #[cfg(not(feature = "tokio"))]
202 pub fn from_root_no_symlinks(path: impl AsRef<Path>) -> std::io::Result<Self> {
203 let base_dir = path.as_ref().to_path_buf();
204 let r = read_dir(path)?;
205
206 Ok(RecursiveDirIterator {
207 base_dir,
208 current_iter: Some(r),
209 dirs: vec![],
210 visit_filter: NoSymlink,
211 })
212 }
213
214 #[cfg(feature = "tokio")]
218 pub async fn from_root_no_symlinks(path: impl AsRef<Path>) -> std::io::Result<Self> {
219 let base_dir = path.as_ref().to_path_buf();
220 let r = read_dir(path).await?;
221
222 Ok(RecursiveDirIterator {
223 base_dir,
224 current_iter: Some(r),
225 dirs: vec![],
226 visit_filter: NoSymlink,
227 })
228 }
229}
230
231impl RecursiveDirIterator<MaxDepth> {
232 #[cfg(not(feature = "tokio"))]
234 pub fn from_root_max_depth(
235 path: impl AsRef<Path>,
236 max_depth: NonZeroUsize,
237 ) -> std::io::Result<Self> {
238 let base_dir = path.as_ref().to_path_buf();
239 let r = read_dir(path)?;
240
241 Ok(RecursiveDirIterator {
242 base_dir,
243 current_iter: Some(r),
244 dirs: vec![],
245 visit_filter: MaxDepth(max_depth),
246 })
247 }
248
249 #[cfg(not(feature = "tokio"))]
251 pub fn from_root_single_depth(path: impl AsRef<Path>) -> std::io::Result<Self> {
252 let base_dir = path.as_ref().to_path_buf();
253 let r = read_dir(path)?;
254
255 Ok(RecursiveDirIterator {
256 base_dir,
257 current_iter: Some(r),
258 dirs: vec![],
259 visit_filter: MaxDepth::single_depth(),
260 })
261 }
262
263 #[cfg(feature = "tokio")]
265 pub async fn from_root_max_depth(
266 path: impl AsRef<Path>,
267 max_depth: NonZeroUsize,
268 ) -> std::io::Result<Self> {
269 let base_dir = path.as_ref().to_path_buf();
270 let r = read_dir(path).await?;
271
272 Ok(RecursiveDirIterator {
273 base_dir,
274 current_iter: Some(r),
275 dirs: vec![],
276 visit_filter: MaxDepth(max_depth),
277 })
278 }
279
280 #[cfg(feature = "tokio")]
282 pub async fn from_root_single_depth(path: impl AsRef<Path>) -> std::io::Result<Self> {
283 let base_dir = path.as_ref().to_path_buf();
284 let r = read_dir(path).await?;
285
286 Ok(RecursiveDirIterator {
287 base_dir,
288 current_iter: Some(r),
289 dirs: vec![],
290 visit_filter: MaxDepth::single_depth(),
291 })
292 }
293}
294
295impl<F> RecursiveDirIterator<Closure<F>> {
296 #[cfg(not(feature = "tokio"))]
299 pub fn with_closure_filter(path: impl AsRef<Path>, filter: F) -> std::io::Result<Self>
300 where
301 F: Into<Closure<F>>,
302 {
303 let base_dir = path.as_ref().to_path_buf();
304 let r = read_dir(path)?;
305
306 Ok(RecursiveDirIterator {
307 base_dir,
308 current_iter: Some(r),
309 dirs: vec![],
310 visit_filter: filter.into(),
311 })
312 }
313
314 #[cfg(feature = "tokio")]
315 pub async fn with_closure_filter(path: impl AsRef<Path>, filter: F) -> std::io::Result<Self>
316 where
317 F: Into<Closure<F>>,
318 {
319 let base_dir = path.as_ref().to_path_buf();
320 let r = read_dir(path).await?;
321
322 Ok(RecursiveDirIterator {
323 base_dir,
324 current_iter: Some(r),
325 dirs: vec![],
326 visit_filter: filter.into(),
327 })
328 }
329}
330
331impl<F> RecursiveDirIterator<This<F>> {
332 #[cfg(not(feature = "tokio"))]
335 pub fn with_filter(path: impl AsRef<Path>, filter: F) -> std::io::Result<Self>
336 where
337 F: Filter,
338 {
339 let base_dir = path.as_ref().to_path_buf();
340 let r = read_dir(path)?;
341
342 Ok(RecursiveDirIterator {
343 base_dir,
344 current_iter: Some(r),
345 dirs: vec![],
346 visit_filter: This::new(filter),
347 })
348 }
349
350 #[cfg(feature = "tokio")]
353 pub async fn with_filter(path: impl AsRef<Path>, filter: F) -> std::io::Result<Self>
354 where
355 F: Filter,
356 {
357 let base_dir = path.as_ref().to_path_buf();
358 let r = read_dir(path).await?;
359
360 Ok(RecursiveDirIterator {
361 base_dir,
362 current_iter: Some(r),
363 dirs: vec![],
364 visit_filter: This::new(filter),
365 })
366 }
367}
368
369impl<S> RecursiveDirIterator<Extension<S>> {
370 #[cfg(not(feature = "tokio"))]
374 pub fn with_extension(path: impl AsRef<Path>, extension: S) -> std::io::Result<Self> {
375 let base_dir = path.as_ref().to_path_buf();
376 let r = read_dir(path)?;
377
378 Ok(RecursiveDirIterator {
379 base_dir,
380 current_iter: Some(r),
381 dirs: vec![],
382 visit_filter: Extension::new(extension),
383 })
384 }
385
386 #[cfg(feature = "tokio")]
390 pub async fn with_extension(path: impl AsRef<Path>, extension: S) -> std::io::Result<Self> {
391 let base_dir = path.as_ref().to_path_buf();
392 let r = read_dir(path).await?;
393
394 Ok(RecursiveDirIterator {
395 base_dir,
396 current_iter: Some(r),
397 dirs: vec![],
398 visit_filter: Extension::new(extension),
399 })
400 }
401}
402
403#[cfg(not(feature = "tokio"))]
404impl<F> Iterator for RecursiveDirIterator<F>
405where
406 F: Filter,
407{
408 type Item = PathBuf;
409
410 fn next(&mut self) -> Option<Self::Item> {
411 fn compute_next_iter<VF>(iter: &mut RecursiveDirIterator<VF>) -> Option<ReadDir> {
412 while let Some(ref p) = iter.dirs.pop() {
413 if let Ok(d) = read_dir(p) {
414 return Some(d);
415 }
416 }
417 None
418 }
419
420 if self.current_iter.is_none() {
421 self.current_iter = compute_next_iter(self);
422 }
423
424 while let Some(ref mut iter) = self.current_iter {
425 for n in iter.flatten().by_ref() {
426 let path = n.path();
427
428 if path.is_dir() && self.visit_filter.filter(&self.base_dir, &path) {
429 self.dirs.push(path.clone());
430 }
431
432 if self.visit_filter.should_emit(&path) {
433 return Some(path.clone());
434 }
435 }
436
437 self.current_iter = compute_next_iter(self);
438 }
439
440 None
441 }
442}
443
444#[cfg(feature = "tokio")]
445impl<F> RecursiveDirIterator<F>
446where
447 F: Filter,
448{
449 pub async fn next(&mut self) -> Option<PathBuf> {
450 async fn compute_next_iter<VF>(iter: &mut RecursiveDirIterator<VF>) -> Option<ReadDir> {
451 while let Some(ref p) = iter.dirs.pop() {
452 if let Ok(d) = read_dir(p).await {
453 return Some(d);
454 }
455 }
456 None
457 }
458
459 if let None = self.current_iter {
460 self.current_iter = compute_next_iter(self).await;
461 }
462
463 while let Some(ref mut iter) = self.current_iter {
464 loop {
465 let next_entry = iter.next_entry().await;
466 match next_entry {
467 Ok(Some(n)) => {
468 let path = n.path();
469
470 if path.is_dir() {
471 if self.visit_filter.filter(&self.base_dir, &path) {
472 self.dirs.push(path.clone());
473 }
474 }
475
476 if self.visit_filter.should_emit(&path) {
477 return Some(path.clone());
478 }
479 }
480 Ok(None) => {
481 break;
482 }
483 Err(_) => {
484 }
486 }
487 }
488
489 self.current_iter = compute_next_iter(self).await;
490 }
491
492 None
493 }
494}
495
496#[cfg(test)]
497#[cfg(not(feature = "tokio"))]
498mod tests {
499 use crate::filter::Extension;
500 use crate::{Filter, MaxDepth, NoSymlink, RecursiveDirIterator};
501 use std::collections::HashSet;
502 use std::num::NonZeroUsize;
503 use std::path::{Path, PathBuf};
504
505 #[test]
506 fn all_files() {
507 let mut dirs = HashSet::new();
508 let from_test_dir = RecursiveDirIterator::from_root("test_dir").unwrap();
509
510 for x in from_test_dir {
511 dirs.insert(x.to_string_lossy().to_string());
512 }
513
514 assert_eq!(dirs.len(), 18);
515 assert!(dirs.contains("test_dir/foo"));
516 assert!(dirs.contains("test_dir/bar"));
517 assert!(dirs.contains("test_dir/baz"));
518 assert!(dirs.contains("test_dir/fus"));
519 assert!(dirs.contains("test_dir/baz/meow"));
520 assert!(dirs.contains("test_dir/baz/meow/oof"));
521 assert!(dirs.contains("test_dir/baz/meow/uuf"));
522 assert!(dirs.contains("test_dir/baz/barbaz"));
523 assert!(dirs.contains("test_dir/baz/foobaz"));
524 assert!(dirs.contains("test_dir/fus/dragonborn"));
525 assert!(dirs.contains("test_dir/fus/dragonborn/witchcraft"));
526 assert!(dirs.contains("test_dir/fus/ro"));
527 assert!(dirs.contains("test_dir/fus/ro/dah"));
528 assert!(dirs.contains("test_dir/fus/ro/dah/dovahkiin"));
529 assert!(dirs.contains("test_dir/one_more_weird_dir"));
530 assert!(dirs.contains("test_dir/one_more_weird_dir/file.i"));
531 assert!(dirs.contains("test_dir/one_more_weird_dir/another_weird_dir.f"));
532 assert!(dirs.contains("test_dir/one_more_weird_dir/another_weird_dir.f/foa"));
533 }
534
535 #[test]
536 fn no_symlinks() {
537 let mut dirs = HashSet::new();
538 let from_test_dir =
539 RecursiveDirIterator::with_closure_filter("test_dir", |_: &Path, p: &Path| {
540 !p.is_symlink()
541 })
542 .unwrap();
543
544 for x in from_test_dir {
545 dirs.insert(x.to_string_lossy().to_string());
546 }
547
548 assert_eq!(dirs.len(), 18);
549 assert!(dirs.contains("test_dir/foo"));
550 assert!(dirs.contains("test_dir/bar"));
551 assert!(dirs.contains("test_dir/baz"));
552 assert!(dirs.contains("test_dir/fus"));
553 assert!(dirs.contains("test_dir/baz/meow"));
554 assert!(dirs.contains("test_dir/baz/meow/oof"));
555 assert!(dirs.contains("test_dir/baz/meow/uuf"));
556 assert!(dirs.contains("test_dir/baz/barbaz"));
557 assert!(dirs.contains("test_dir/baz/foobaz"));
558 assert!(dirs.contains("test_dir/fus/dragonborn"));
559 assert!(dirs.contains("test_dir/fus/dragonborn/witchcraft"));
560 assert!(dirs.contains("test_dir/fus/ro"));
561 assert!(dirs.contains("test_dir/fus/ro/dah"));
562 assert!(dirs.contains("test_dir/fus/ro/dah/dovahkiin"));
563 assert!(dirs.contains("test_dir/one_more_weird_dir"));
564 assert!(dirs.contains("test_dir/one_more_weird_dir/file.i"));
565 assert!(dirs.contains("test_dir/one_more_weird_dir/another_weird_dir.f"));
566 assert!(dirs.contains("test_dir/one_more_weird_dir/another_weird_dir.f/foa"));
567 }
568
569 #[test]
570 fn depth_1() {
571 let mut dirs = HashSet::new();
572
573 let root = PathBuf::from("test_dir");
574 let ancestors = root.ancestors().count();
575
576 let from_test_dir =
577 RecursiveDirIterator::with_closure_filter(&root, |_: &Path, p: &Path| {
578 p.ancestors().count() - ancestors == 0
579 })
580 .unwrap();
581
582 for x in from_test_dir {
583 dirs.insert(x.to_string_lossy().to_string());
584 }
585
586 assert_eq!(dirs.len(), 5);
587 assert!(dirs.contains("test_dir/foo"));
588 assert!(dirs.contains("test_dir/bar"));
589 assert!(dirs.contains("test_dir/baz"));
590 assert!(dirs.contains("test_dir/fus"));
591 assert!(dirs.contains("test_dir/one_more_weird_dir"));
592 }
593
594 #[test]
595 fn depth_2() {
596 let mut dirs = HashSet::new();
597
598 let root = PathBuf::from("test_dir");
599 let ancestors = root.ancestors().count();
600
601 let from_test_dir =
602 RecursiveDirIterator::with_closure_filter(&root, |_: &Path, p: &Path| {
603 p.ancestors().count() - ancestors <= 1
604 })
605 .unwrap();
606
607 for x in from_test_dir {
608 dirs.insert(x.to_string_lossy().to_string());
609 }
610
611 assert_eq!(dirs.len(), 12);
612 assert!(dirs.contains("test_dir/foo"));
613 assert!(dirs.contains("test_dir/bar"));
614 assert!(dirs.contains("test_dir/baz"));
615 assert!(dirs.contains("test_dir/fus"));
616 assert!(dirs.contains("test_dir/baz/meow"));
617 assert!(dirs.contains("test_dir/baz/barbaz"));
618 assert!(dirs.contains("test_dir/baz/foobaz"));
619 assert!(dirs.contains("test_dir/fus/dragonborn"));
620 assert!(dirs.contains("test_dir/fus/ro"));
621 assert!(dirs.contains("test_dir/one_more_weird_dir"));
622 assert!(dirs.contains("test_dir/one_more_weird_dir/file.i"));
623 }
624
625 #[test]
626 fn no_symlink_depth_2() {
627 let mut dirs = HashSet::new();
628
629 let root = PathBuf::from("test_dir");
630 let _ancestors = root.ancestors().count();
631
632 let from_test_dir = RecursiveDirIterator::with_filter(
633 &root,
634 NoSymlink.and(MaxDepth::new(NonZeroUsize::new(2).unwrap())),
635 )
636 .unwrap();
637
638 for x in from_test_dir {
639 dirs.insert(x.to_string_lossy().to_string());
640 }
641
642 assert_eq!(dirs.len(), 12);
643 assert!(dirs.contains("test_dir/foo"));
644 assert!(dirs.contains("test_dir/bar"));
645 assert!(dirs.contains("test_dir/baz"));
646 assert!(dirs.contains("test_dir/fus"));
647 assert!(dirs.contains("test_dir/baz/meow"));
648 assert!(dirs.contains("test_dir/baz/barbaz"));
649 assert!(dirs.contains("test_dir/baz/foobaz"));
650 assert!(dirs.contains("test_dir/fus/dragonborn"));
651 assert!(dirs.contains("test_dir/fus/ro"));
652 assert!(dirs.contains("test_dir/one_more_weird_dir"));
653 assert!(dirs.contains("test_dir/one_more_weird_dir/file.i"));
654 }
655
656 #[test]
657 fn file_with_extension() {
658 let mut dirs = Vec::new();
659
660 let root = PathBuf::from("test_dir");
661
662 let from_test_dir = RecursiveDirIterator::with_filter(&root, Extension::new("i")).unwrap();
663
664 for x in from_test_dir {
665 dirs.push(x.to_string_lossy().to_string());
666 }
667
668 dirs.sort();
669
670 assert_eq!(dirs.len(), 1);
671 assert_eq!(
672 dirs,
673 vec![String::from("test_dir/one_more_weird_dir/file.i")]
674 );
675 }
676
677 #[test]
678 fn path_with_extension() {
679 let mut dirs = Vec::new();
680
681 let root = PathBuf::from("test_dir");
682
683 let from_test_dir = RecursiveDirIterator::with_filter(&root, Extension::new("f")).unwrap();
684
685 for x in from_test_dir {
686 dirs.push(x.to_string_lossy().to_string());
687 }
688
689 dirs.sort();
690
691 assert_eq!(dirs.len(), 1);
692 assert_eq!(
693 dirs,
694 vec![String::from(
695 "test_dir/one_more_weird_dir/another_weird_dir.f"
696 )]
697 );
698 }
699}
700
701#[cfg(test)]
702#[cfg(feature = "tokio")]
703mod tests {
704 use crate::filter::Extension;
705 use crate::{Filter, MaxDepth, NoSymlink, RecursiveDirIterator};
706 use std::collections::HashSet;
707 use std::num::NonZeroUsize;
708 use std::path::{Path, PathBuf};
709
710 #[tokio::test]
711 async fn tokio_all_files() {
712 let mut dirs = HashSet::new();
713 let mut from_test_dir = RecursiveDirIterator::from_root("test_dir").await.unwrap();
714
715 while let Some(x) = from_test_dir.next().await {
716 dirs.insert(x.to_string_lossy().to_string());
717 }
718
719 assert_eq!(dirs.len(), 18);
720 assert!(dirs.contains("test_dir/foo"));
721 assert!(dirs.contains("test_dir/bar"));
722 assert!(dirs.contains("test_dir/baz"));
723 assert!(dirs.contains("test_dir/fus"));
724 assert!(dirs.contains("test_dir/baz/meow"));
725 assert!(dirs.contains("test_dir/baz/meow/oof"));
726 assert!(dirs.contains("test_dir/baz/meow/uuf"));
727 assert!(dirs.contains("test_dir/baz/barbaz"));
728 assert!(dirs.contains("test_dir/baz/foobaz"));
729 assert!(dirs.contains("test_dir/fus/dragonborn"));
730 assert!(dirs.contains("test_dir/fus/dragonborn/witchcraft"));
731 assert!(dirs.contains("test_dir/fus/ro"));
732 assert!(dirs.contains("test_dir/fus/ro/dah"));
733 assert!(dirs.contains("test_dir/fus/ro/dah/dovahkiin"));
734 assert!(dirs.contains("test_dir/one_more_weird_dir"));
735 assert!(dirs.contains("test_dir/one_more_weird_dir/file.i"));
736 assert!(dirs.contains("test_dir/one_more_weird_dir/another_weird_dir.f"));
737 assert!(dirs.contains("test_dir/one_more_weird_dir/another_weird_dir.f/foa"));
738 }
739
740 #[tokio::test]
741 async fn tokio_no_symlinks() {
742 let mut dirs = HashSet::new();
743 let mut from_test_dir =
744 RecursiveDirIterator::with_closure_filter("test_dir", |_: &Path, p: &Path| {
745 !p.is_symlink()
746 })
747 .await
748 .unwrap();
749
750 while let Some(x) = from_test_dir.next().await {
751 dirs.insert(x.to_string_lossy().to_string());
752 }
753
754 assert_eq!(dirs.len(), 18);
755 assert!(dirs.contains("test_dir/foo"));
756 assert!(dirs.contains("test_dir/bar"));
757 assert!(dirs.contains("test_dir/baz"));
758 assert!(dirs.contains("test_dir/fus"));
759 assert!(dirs.contains("test_dir/baz/meow"));
760 assert!(dirs.contains("test_dir/baz/meow/oof"));
761 assert!(dirs.contains("test_dir/baz/meow/uuf"));
762 assert!(dirs.contains("test_dir/baz/barbaz"));
763 assert!(dirs.contains("test_dir/baz/foobaz"));
764 assert!(dirs.contains("test_dir/fus/dragonborn"));
765 assert!(dirs.contains("test_dir/fus/dragonborn/witchcraft"));
766 assert!(dirs.contains("test_dir/fus/ro"));
767 assert!(dirs.contains("test_dir/fus/ro/dah"));
768 assert!(dirs.contains("test_dir/fus/ro/dah/dovahkiin"));
769 assert!(dirs.contains("test_dir/one_more_weird_dir"));
770 assert!(dirs.contains("test_dir/one_more_weird_dir/file.i"));
771 assert!(dirs.contains("test_dir/one_more_weird_dir/another_weird_dir.f"));
772 assert!(dirs.contains("test_dir/one_more_weird_dir/another_weird_dir.f/foa"));
773 }
774
775 #[tokio::test]
776 async fn tokio_depth_1() {
777 let mut dirs = HashSet::new();
778
779 let root = PathBuf::from("test_dir");
780 let ancestors = root.ancestors().count();
781
782 let mut from_test_dir =
783 RecursiveDirIterator::with_closure_filter(&root, |_: &Path, p: &Path| {
784 p.ancestors().count() - ancestors <= 0
785 })
786 .await
787 .unwrap();
788
789 while let Some(x) = from_test_dir.next().await {
790 dirs.insert(x.to_string_lossy().to_string());
791 }
792
793 assert_eq!(dirs.len(), 5);
794 assert!(dirs.contains("test_dir/foo"));
795 assert!(dirs.contains("test_dir/bar"));
796 assert!(dirs.contains("test_dir/baz"));
797 assert!(dirs.contains("test_dir/fus"));
798 assert!(dirs.contains("test_dir/one_more_weird_dir"));
799 }
800
801 #[tokio::test]
802 async fn tokio_depth_2() {
803 let mut dirs = HashSet::new();
804
805 let root = PathBuf::from("test_dir");
806 let ancestors = root.ancestors().count();
807
808 let mut from_test_dir =
809 RecursiveDirIterator::with_closure_filter(&root, |_: &Path, p: &Path| {
810 p.ancestors().count() - ancestors <= 1
811 })
812 .await
813 .unwrap();
814
815 while let Some(x) = from_test_dir.next().await {
816 dirs.insert(x.to_string_lossy().to_string());
817 }
818
819 assert_eq!(dirs.len(), 12);
820 assert!(dirs.contains("test_dir/foo"));
821 assert!(dirs.contains("test_dir/bar"));
822 assert!(dirs.contains("test_dir/baz"));
823 assert!(dirs.contains("test_dir/fus"));
824 assert!(dirs.contains("test_dir/baz/meow"));
825 assert!(dirs.contains("test_dir/baz/barbaz"));
826 assert!(dirs.contains("test_dir/baz/foobaz"));
827 assert!(dirs.contains("test_dir/fus/dragonborn"));
828 assert!(dirs.contains("test_dir/fus/ro"));
829 assert!(dirs.contains("test_dir/one_more_weird_dir"));
830 assert!(dirs.contains("test_dir/one_more_weird_dir/file.i"));
831 }
832
833 #[tokio::test]
834 async fn tokio_no_symlink_depth_2() {
835 let mut dirs = HashSet::new();
836
837 let root = PathBuf::from("test_dir");
838 let _ancestors = root.ancestors().count();
839
840 let mut from_test_dir = RecursiveDirIterator::with_filter(
841 &root,
842 NoSymlink.and(MaxDepth::new(NonZeroUsize::new(2).unwrap())),
843 )
844 .await
845 .unwrap();
846
847 while let Some(x) = from_test_dir.next().await {
848 dirs.insert(x.to_string_lossy().to_string());
849 }
850
851 assert_eq!(dirs.len(), 12);
852 assert!(dirs.contains("test_dir/foo"));
853 assert!(dirs.contains("test_dir/bar"));
854 assert!(dirs.contains("test_dir/baz"));
855 assert!(dirs.contains("test_dir/fus"));
856 assert!(dirs.contains("test_dir/baz/meow"));
857 assert!(dirs.contains("test_dir/baz/barbaz"));
858 assert!(dirs.contains("test_dir/baz/foobaz"));
859 assert!(dirs.contains("test_dir/fus/dragonborn"));
860 assert!(dirs.contains("test_dir/fus/ro"));
861 assert!(dirs.contains("test_dir/one_more_weird_dir"));
862 assert!(dirs.contains("test_dir/one_more_weird_dir/file.i"));
863 }
864
865 #[tokio::test]
866 async fn tokio_file_with_extension() {
867 let mut dirs = Vec::new();
868
869 let root = PathBuf::from("test_dir");
870
871 let mut from_test_dir = RecursiveDirIterator::with_filter(&root, Extension::new("i"))
872 .await
873 .unwrap();
874
875 while let Some(x) = from_test_dir.next().await {
876 dirs.push(x.to_string_lossy().to_string());
877 }
878
879 dirs.sort();
880
881 assert_eq!(dirs.len(), 1);
882 assert_eq!(
883 dirs,
884 vec![String::from("test_dir/one_more_weird_dir/file.i")]
885 );
886 }
887
888 #[tokio::test]
889 async fn tokio_path_with_extension() {
890 let mut dirs = Vec::new();
891
892 let root = PathBuf::from("test_dir");
893
894 let mut from_test_dir = RecursiveDirIterator::with_filter(&root, Extension::new("f"))
895 .await
896 .unwrap();
897
898 while let Some(x) = from_test_dir.next().await {
899 dirs.push(x.to_string_lossy().to_string());
900 }
901
902 dirs.sort();
903
904 assert_eq!(dirs.len(), 1);
905 assert_eq!(
906 dirs,
907 vec![String::from(
908 "test_dir/one_more_weird_dir/another_weird_dir.f"
909 )]
910 );
911 }
912}