1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use crate::constants;
use std::{
    ffi::OsStr,
    fs,
    path::{Path, PathBuf},
};

pub fn get_sway_files(path: PathBuf) -> Vec<PathBuf> {
    let mut files = vec![];
    let mut dir_entries = vec![path];

    while let Some(next_dir) = dir_entries.pop() {
        if let Ok(read_dir) = fs::read_dir(next_dir) {
            for entry in read_dir.filter_map(|res| res.ok()) {
                let path = entry.path();

                if path.is_dir() {
                    dir_entries.push(path);
                } else if is_sway_file(&path) {
                    files.push(path)
                }
            }
        }
    }

    files
}

pub fn is_sway_file(file: &Path) -> bool {
    let res = file.extension();
    file.is_file() && Some(OsStr::new(constants::SWAY_EXTENSION)) == res
}

/// create an iterator over all prefixes in a slice, smallest first
///
/// ```
/// # use sway_utils::iter_prefixes;
/// let val = [1, 2, 3];
/// let mut it = iter_prefixes(&val);
/// assert_eq!(it.next(), Some([1].as_slice()));
/// assert_eq!(it.next(), Some([1, 2].as_slice()));
/// assert_eq!(it.next(), Some([1, 2, 3].as_slice()));
/// assert_eq!(it.next(), None);
///
/// ```
pub fn iter_prefixes<T>(slice: &[T]) -> impl Iterator<Item = &[T]> + DoubleEndedIterator {
    (1..=slice.len()).map(move |len| &slice[..len])
}

/// Continually go down in the file tree until a Forc manifest file is found.
pub fn find_nested_manifest_dir(starter_path: &Path) -> Option<PathBuf> {
    find_nested_dir_with_file(starter_path, constants::MANIFEST_FILE_NAME)
}

/// Continually go down in the file tree until a specified file is found.
///
/// Starts the search from child dirs of `starter_path`.
pub fn find_nested_dir_with_file(starter_path: &Path, file_name: &str) -> Option<PathBuf> {
    use walkdir::WalkDir;
    let starter_dir = if starter_path.is_dir() {
        starter_path
    } else {
        starter_path.parent()?
    };
    WalkDir::new(starter_path)
        .into_iter()
        .filter_map(|e| {
            let entry = e.ok()?;
            if entry.path() != starter_dir.join(file_name)
                && entry.file_name().to_string_lossy() == file_name
            {
                let mut entry = entry.path().to_path_buf();
                entry.pop();
                Some(entry)
            } else {
                None
            }
        })
        .next()
}

/// Continually go up in the file tree until a specified file is found.
///
/// Starts the search from `starter_path`.
#[allow(clippy::branches_sharing_code)]
pub fn find_parent_dir_with_file<P: AsRef<Path>>(
    starter_path: P,
    file_name: &str,
) -> Option<PathBuf> {
    let mut path = std::fs::canonicalize(starter_path).ok()?;
    let empty_path = PathBuf::from("/");
    while path != empty_path {
        path.push(file_name);
        if path.exists() {
            path.pop();
            return Some(path);
        } else {
            path.pop();
            path.pop();
        }
    }
    None
}
/// Continually go up in the file tree until a Forc manifest file is found.
pub fn find_parent_manifest_dir<P: AsRef<Path>>(starter_path: P) -> Option<PathBuf> {
    find_parent_dir_with_file(starter_path, constants::MANIFEST_FILE_NAME)
}

/// Continually go up in the file tree until a Forc manifest file is found and given predicate
/// returns true.
pub fn find_parent_manifest_dir_with_check<T: AsRef<Path>, F>(
    starter_path: T,
    f: F,
) -> Option<PathBuf>
where
    F: Fn(&Path) -> bool,
{
    find_parent_manifest_dir(starter_path).and_then(|manifest_dir| {
        // If given check satisifies return current dir otherwise start searching from the parent.
        if f(&manifest_dir) {
            Some(manifest_dir)
        } else if let Some(parent_dir) = manifest_dir.parent() {
            find_parent_manifest_dir_with_check(parent_dir, f)
        } else {
            None
        }
    })
}