sway_utils/
helpers.rs

1use crate::constants;
2use std::{
3    ffi::OsStr,
4    fs,
5    path::{Path, PathBuf},
6};
7use walkdir::WalkDir;
8
9pub fn get_sway_files(path: PathBuf) -> Vec<PathBuf> {
10    let mut files = vec![];
11    let mut dir_entries = vec![path];
12
13    while let Some(next_dir) = dir_entries.pop() {
14        if let Ok(read_dir) = fs::read_dir(&next_dir) {
15            for entry in read_dir.filter_map(Result::ok) {
16                let path = entry.path();
17                if path.is_dir() {
18                    dir_entries.push(path);
19                } else if is_sway_file(&path) {
20                    files.push(path);
21                }
22            }
23        }
24    }
25    files
26}
27
28pub fn is_sway_file(file: &Path) -> bool {
29    file.is_file() && file.extension() == Some(OsStr::new(constants::SWAY_EXTENSION))
30}
31
32/// Create an iterator over all prefixes in a slice, smallest first
33///
34/// ```
35/// # use sway_utils::iter_prefixes;
36/// let val = [1, 2, 3];
37/// let mut it = iter_prefixes(&val);
38/// assert_eq!(it.next(), Some([1].as_slice()));
39/// assert_eq!(it.next(), Some([1, 2].as_slice()));
40/// assert_eq!(it.next(), Some([1, 2, 3].as_slice()));
41/// assert_eq!(it.next(), None);
42/// ```
43pub fn iter_prefixes<T>(slice: &[T]) -> impl DoubleEndedIterator<Item = &[T]> {
44    (1..=slice.len()).map(move |len| &slice[..len])
45}
46
47/// Continually go down in the file tree until a Forc manifest file is found.
48pub fn find_nested_manifest_dir(starter_path: &Path) -> Option<PathBuf> {
49    find_nested_dir_with_file(starter_path, constants::MANIFEST_FILE_NAME)
50}
51
52/// Continually go down in the file tree until a specified file is found.
53///
54/// Starts the search from child dirs of `starter_path`.
55pub fn find_nested_dir_with_file(starter_path: &Path, file_name: &str) -> Option<PathBuf> {
56    let starter_dir = if starter_path.is_dir() {
57        starter_path
58    } else {
59        starter_path.parent()?
60    };
61    WalkDir::new(starter_path).into_iter().find_map(|e| {
62        let entry = e.ok()?;
63        if entry.path() != starter_dir.join(file_name) && entry.file_name() == OsStr::new(file_name)
64        {
65            let mut entry = entry.path().to_path_buf();
66            entry.pop();
67            Some(entry)
68        } else {
69            None
70        }
71    })
72}
73
74/// Continually go up in the file tree until a specified file is found.
75///
76/// Starts the search from `starter_path`.
77pub fn find_parent_dir_with_file<P: AsRef<Path>>(
78    starter_path: P,
79    file_name: &str,
80) -> Option<PathBuf> {
81    let mut path = std::fs::canonicalize(starter_path).ok()?;
82    let root_path = PathBuf::from("/");
83    while path != root_path {
84        path.push(file_name);
85        if path.exists() {
86            path.pop();
87            return Some(path);
88        }
89        path.pop();
90        path.pop();
91    }
92    None
93}
94
95/// Continually go up in the file tree until a Forc manifest file is found.
96pub fn find_parent_manifest_dir<P: AsRef<Path>>(starter_path: P) -> Option<PathBuf> {
97    find_parent_dir_with_file(starter_path, constants::MANIFEST_FILE_NAME)
98}
99
100/// Continually go up in the file tree until a Forc manifest file is found and the given predicate
101/// returns true.
102pub fn find_parent_manifest_dir_with_check<T: AsRef<Path>, F>(
103    starter_path: T,
104    check: F,
105) -> Option<PathBuf>
106where
107    F: Fn(&Path) -> bool,
108{
109    find_parent_manifest_dir(&starter_path).and_then(|manifest_dir| {
110        // If given check satisfies, return the current dir; otherwise, start searching from the parent.
111        if check(&manifest_dir) {
112            Some(manifest_dir)
113        } else {
114            manifest_dir
115                .parent()
116                .and_then(|parent_dir| find_parent_manifest_dir_with_check(parent_dir, check))
117        }
118    })
119}