Coverage Report

Created: 2026-10-28 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/ignore-0.4.31/src/pathutil.rs
Line
Count
Source
1
use std::{ffi::OsStr, path::Path};
2
3
use crate::walk::DirEntry;
4
5
/// Returns true if and only if this path is considered to be hidden.
6
///
7
/// # Platform behavior
8
///
9
/// ## Windows
10
///
11
/// This returns true if one of the following is true:
12
///
13
/// * The base name of the path starts with a `.`.
14
/// * The file attributes have the `HIDDEN` property set.
15
///
16
/// ## All other platforms
17
///
18
/// This only returns true if the base name of the path starts with a `.`.
19
0
pub(crate) fn is_hidden_path(dent: &Path) -> bool {
20
    #[cfg(not(windows))]
21
0
    fn imp(path: &Path) -> bool {
22
0
        is_hidden_path_only(path)
23
0
    }
24
25
    #[cfg(windows)]
26
    fn imp(path: &Path) -> bool {
27
        use std::os::windows::fs::MetadataExt;
28
        use winapi_util::file;
29
30
        if let Ok(md) = path.metadata() {
31
            if file::is_hidden(md.file_attributes() as u64) {
32
                return true;
33
            }
34
        }
35
        is_hidden_path_only(path)
36
    }
37
38
0
    imp(dent)
39
0
}
40
41
/// Returns true if and only if this directory entry is considered to be
42
/// hidden.
43
///
44
/// # Platform behavior
45
///
46
/// ## Windows
47
///
48
/// This returns true if one of the following is true:
49
///
50
/// * The base name of the path starts with a `.`.
51
/// * The file attributes have the `HIDDEN` property set.
52
///
53
/// ## All other platforms
54
///
55
/// This only returns true if the base name of the path starts with a `.`.
56
0
pub(crate) fn is_hidden_entry(dent: &DirEntry) -> bool {
57
    #[cfg(not(windows))]
58
0
    fn imp(dent: &DirEntry) -> bool {
59
0
        is_hidden_path_only(dent.path())
60
0
    }
61
62
    #[cfg(windows)]
63
    fn imp(dent: &DirEntry) -> bool {
64
        use std::os::windows::fs::MetadataExt;
65
        use winapi_util::file;
66
67
        // This looks like we're doing an extra stat call, but on Windows, the
68
        // directory traverser reuses the metadata retrieved from each directory
69
        // entry and stores it on the DirEntry itself. So this is "free."
70
        if let Ok(md) = dent.metadata() {
71
            if file::is_hidden(md.file_attributes() as u64) {
72
                return true;
73
            }
74
        }
75
        is_hidden_path_only(dent.path())
76
    }
77
78
0
    imp(dent)
79
0
}
80
81
/// Returns true if and only if this path is considered to be hidden from only
82
/// the path itself.
83
///
84
/// This has the same behavior on all platforms.
85
0
fn is_hidden_path_only(path: &Path) -> bool {
86
0
    if let Some(name) = file_name(path) {
87
0
        name.as_encoded_bytes().starts_with(b".")
88
    } else {
89
0
        false
90
    }
91
0
}
92
93
/// Strip `prefix` from the `path` and return the remainder.
94
///
95
/// If `path` doesn't have a prefix `prefix`, then return `None`.
96
0
pub(crate) fn strip_prefix<'a, P: AsRef<Path> + ?Sized>(
97
0
    prefix: &'a P,
98
0
    path: &'a Path,
99
0
) -> Option<&'a Path> {
100
    #[cfg(unix)]
101
0
    fn imp<'a>(prefix: &'a Path, path: &'a Path) -> Option<&'a Path> {
102
        use std::os::unix::ffi::OsStrExt;
103
104
0
        let prefix = prefix.as_os_str().as_bytes();
105
0
        let path = path.as_os_str().as_bytes();
106
0
        if prefix.len() > path.len() || prefix != &path[0..prefix.len()] {
107
0
            None
108
        } else {
109
0
            Some(&Path::new(OsStr::from_bytes(&path[prefix.len()..])))
110
        }
111
0
    }
112
113
    #[cfg(not(unix))]
114
    fn imp<'a>(prefix: &'a Path, path: &'a Path) -> Option<&'a Path> {
115
        path.strip_prefix(prefix).ok()
116
    }
117
118
0
    imp(prefix.as_ref(), path)
119
0
}
Unexecuted instantiation: ignore::pathutil::strip_prefix::<std::path::Path>
Unexecuted instantiation: ignore::pathutil::strip_prefix::<std::path::PathBuf>
Unexecuted instantiation: ignore::pathutil::strip_prefix::<str>
120
121
/// Returns true if this file path is just a file name. i.e., Its parent is
122
/// the empty string.
123
0
pub(crate) fn is_file_name<P: AsRef<Path>>(path: P) -> bool {
124
    #[cfg(unix)]
125
    {
126
0
        memchr::memchr(b'/', path.as_ref().as_os_str().as_encoded_bytes())
127
0
            .is_none()
128
    }
129
    #[cfg(not(unix))]
130
    {
131
        path.as_ref()
132
            .parent()
133
            .map(|p| p.as_os_str().is_empty())
134
            .unwrap_or(false)
135
    }
136
0
}
137
138
/// The final component of the path, if it is a normal file.
139
///
140
/// If the path terminates in `.`, `..`, or consists solely of a root of
141
/// prefix, this will return `None`.
142
0
pub(crate) fn file_name<'a, P: AsRef<Path> + ?Sized>(
143
0
    path: &'a P,
144
0
) -> Option<&'a OsStr> {
145
    #[cfg(unix)]
146
0
    fn imp(path: &Path) -> Option<&OsStr> {
147
        use std::os::unix::ffi::OsStrExt;
148
149
        use memchr::memrchr;
150
151
0
        let path = path.as_os_str().as_bytes();
152
0
        if path.is_empty() {
153
0
            return None;
154
0
        } else if path.len() == 1 && path[0] == b'.' {
155
0
            return None;
156
0
        } else if path.last() == Some(&b'.') {
157
0
            return None;
158
0
        } else if path.len() >= 2 && &path[path.len() - 2..] == &b".."[..] {
159
0
            return None;
160
0
        }
161
0
        let last_slash = memrchr(b'/', path).map(|i| i + 1).unwrap_or(0);
162
0
        Some(OsStr::from_bytes(&path[last_slash..]))
163
0
    }
164
165
    #[cfg(not(unix))]
166
    fn imp(path: &Path) -> Option<&OsStr> {
167
        path.file_name()
168
    }
169
170
0
    imp(path.as_ref())
171
0
}