freedesktop_desktop_entry/
iter.rs

1// Copyright 2021 System76 <info@system76.com>
2// SPDX-License-Identifier: MPL-2.0
3
4use std::{collections::VecDeque, fs, path::PathBuf};
5
6use crate::DesktopEntry;
7
8pub struct Iter {
9    directories_to_walk: VecDeque<PathBuf>,
10    actively_walking: Option<fs::ReadDir>,
11}
12
13impl Iter {
14    /// Directories will be processed in order.
15    pub fn new<I: Iterator<Item = PathBuf>>(directories_to_walk: I) -> Self {
16        Self {
17            directories_to_walk: directories_to_walk.collect(),
18            actively_walking: None,
19        }
20    }
21}
22
23impl Iterator for Iter {
24    type Item = PathBuf;
25
26    fn next(&mut self) -> Option<Self::Item> {
27        'outer: loop {
28            let mut iterator = match self.actively_walking.take() {
29                Some(dir) => dir,
30                None => {
31                    while let Some(path) = self.directories_to_walk.pop_front() {
32                        match fs::read_dir(&path) {
33                            Ok(directory) => {
34                                self.actively_walking = Some(directory);
35                                continue 'outer;
36                            }
37
38                            // Skip directories_to_walk which could not be read
39                            Err(_) => continue,
40                        }
41                    }
42
43                    return None;
44                }
45            };
46
47            'inner: while let Some(entry) = iterator.next() {
48                if let Ok(entry) = entry {
49                    let mut path = entry.path();
50
51                    path = match path.canonicalize() {
52                        Ok(canonicalized) => canonicalized,
53                        Err(_) => continue 'inner,
54                    };
55
56                    if let Ok(metadata) = path.metadata() {
57                        if metadata.is_dir() {
58                            self.directories_to_walk.push_front(path);
59                        } else if metadata.is_file()
60                            && path.extension().map_or(false, |ext| ext == "desktop")
61                        {
62                            self.actively_walking = Some(iterator);
63                            return Some(path);
64                        }
65                    }
66                }
67            }
68        }
69    }
70}
71
72impl Iter {
73    pub fn entries<'i, 'l: 'i, L>(
74        self,
75        locales_filter: Option<&'l [L]>,
76    ) -> impl Iterator<Item = DesktopEntry> + 'i
77    where
78        L: AsRef<str>,
79    {
80        self.map(move |path| DesktopEntry::from_path(path, locales_filter))
81            .filter_map(|e| e.ok())
82    }
83}