1use regex::Regex;
2
3pub fn is_absolute(path: &str) -> bool {
4 path.starts_with('/')
5 || Regex::new(r"(^(\\\\\?\\)?[a-zA-Z]:)|(^\\\\\?\\[a-zA-Z]{1,})")
6 .expect(&t!("errors.invalid_regex"))
7 .is_match(path)
8}
9
10pub fn is_root(path: &str) -> bool {
11 path == "/"
12 || Regex::new(r"(^(\\\\\?\\)?[a-zA-Z]:\\?$)|(^\\\\\?\\[a-zA-Z]{1,}\\?$)")
13 .expect(&t!("errors.invalid_regex"))
14 .is_match(path)
15}
16
17pub fn parent(path: &str) -> Option<&str> {
18 if is_root(path) {
19 return None;
20 }
21 let path = path.trim_end_matches(['/', '\\']);
22 let last_delimiter_index = path.rfind(['/', '\\']);
23 Some(path.split_at(last_delimiter_index? + 1).0)
24}
25
26pub fn join(start: &str, end: &str, separator: char) -> String {
27 let start = start.trim_end_matches(separator);
28 let end = end.trim_start_matches(separator);
29 if end == ".." {
30 parent(start).unwrap_or(start).to_string()
31 } else if end == "." {
32 start.to_string()
33 } else {
34 format!("{start}{separator}{end}")
35 }
36}
37
38pub fn filename(path: &str) -> Option<&str> {
39 if is_root(path) {
40 None
41 } else {
42 let path = path.trim_end_matches(['/', '\\']);
43 path.rsplit(['/', '\\']).next()
44 }
45}
46
47pub fn diff<'a>(full_path: &'a str, prefix_path: &str) -> &'a str {
48 if full_path == prefix_path {
49 "."
50 } else if let Some(unprefixed_path) = full_path.strip_prefix(prefix_path) {
51 unprefixed_path.trim_start_matches(['/', '\\'])
52 } else if let Some(unprefixed_path_reverse_logic) = prefix_path.strip_prefix(full_path) {
53 if unprefixed_path_reverse_logic
54 .split(['/', '\\'])
55 .filter(|s| !s.is_empty())
56 .count()
57 == 1
58 {
59 ".."
60 } else {
61 full_path
62 }
63 } else {
64 full_path
65 }
66}
67
68#[cfg(test)]
69mod test {
70 use super::*;
71
72 #[test]
73 fn test_is_absolute() {
74 assert!(is_absolute("/"));
75 assert!(!is_absolute("test"));
76 assert!(is_absolute("/home/user"));
77 assert!(!is_absolute("home/user"));
78
79 assert!(!is_absolute("\\\\?\\"));
80 assert!(is_absolute("\\\\?\\C:\\Users\\"));
81 assert!(is_absolute("\\\\?\\d:\\Users"));
82 assert!(is_absolute("C:\\Users"));
83 assert!(is_absolute("d:\\Users"));
84 assert!(!is_absolute("ASDF\\Users"));
85 assert!(is_absolute("\\\\?\\ASDF\\"));
86 assert!(!is_absolute(".\\Users"));
87 }
88
89 #[test]
90 fn test_is_root() {
91 assert!(is_root("/"));
92 assert!(!is_root("test"));
93 assert!(!is_root("/home/user"));
94 assert!(!is_root("home/user"));
95
96 assert!(!is_root("\\\\?\\"));
97 assert!(is_root("\\\\?\\C:\\"));
98 assert!(is_root("\\\\?\\d:"));
99 assert!(is_root("C:\\"));
100 assert!(is_root("d:"));
101 assert!(!is_root("ASDF\\"));
102 assert!(is_root("\\\\?\\ASDF\\"));
103 assert!(!is_root(".\\Users"));
104 }
105
106 #[test]
107 fn test_parent() {
108 assert_eq!(parent("/home/user"), Some("/home/"));
109 assert_eq!(parent("/home"), Some("/"));
110 assert_eq!(parent("/"), None);
111 assert_eq!(parent("C:\\Users\\user"), Some("C:\\Users\\"));
112 assert_eq!(parent("C:\\Users"), Some("C:\\"));
113 assert_eq!(parent("C:\\"), None);
114 assert_eq!(parent("\\\\?\\C:\\Users\\user"), Some("\\\\?\\C:\\Users\\"));
115 assert_eq!(parent("\\\\?\\C:\\Users"), Some("\\\\?\\C:\\"));
116 assert_eq!(parent("\\\\?\\C:\\"), None);
117 assert_eq!(
118 parent("\\\\?\\ASDF\\Users\\user"),
119 Some("\\\\?\\ASDF\\Users\\")
120 );
121 assert_eq!(parent("\\\\?\\ASDF\\Users"), Some("\\\\?\\ASDF\\"));
122 assert_eq!(parent("\\\\?\\ASDF"), None);
123 }
124
125 #[test]
126 fn test_join() {
127 assert_eq!(join("/home", "user", '/'), "/home/user");
128 assert_eq!(join("/home", "..", '/'), "/");
129 assert_eq!(join("/home", ".", '/'), "/home");
130
131 assert_eq!(join("C:\\Users", "user", '\\'), "C:\\Users\\user");
132 assert_eq!(join("C:\\Users", "..", '\\'), "C:\\");
133 assert_eq!(join("C:\\Users", ".", '\\'), "C:\\Users");
134 }
135
136 #[test]
137 fn test_filename() {
138 assert_eq!(filename("/home/user"), Some("user"));
139 assert_eq!(filename("/home"), Some("home"));
140 assert_eq!(filename("/"), None);
141 assert_eq!(filename("C:\\Users\\user"), Some("user"));
142 assert_eq!(filename("C:\\Users"), Some("Users"));
143 assert_eq!(filename("C:\\"), None);
144 assert_eq!(filename("\\\\?\\C:\\Users\\user"), Some("user"));
145 assert_eq!(filename("\\\\?\\C:\\Users"), Some("Users"));
146 assert_eq!(filename("\\\\?\\C:\\"), None);
147 assert_eq!(filename("\\\\?\\ASDF\\Users\\user"), Some("user"));
148 assert_eq!(filename("\\\\?\\ASDF\\Users"), Some("Users"));
149 assert_eq!(filename("\\\\?\\ASDF"), None);
150 }
151
152 #[test]
153 fn test_diff() {
154 assert_eq!(diff("/home/user", "/home"), "user");
155 assert_eq!(diff("/home", "/home/user"), "..");
156 assert_eq!(diff("/home", "/home"), ".");
157 assert_eq!(diff("/home", "/"), "home");
158 assert_eq!(diff("/", "/"), ".");
159 assert_eq!(diff("C:\\Users\\user", "C:\\Users"), "user");
160 assert_eq!(diff("C:\\Users", "C:\\Users\\user"), "..");
161 assert_eq!(diff("C:\\Users", "C:\\Users"), ".");
162 assert_eq!(diff("C:\\Users", "C:\\"), "Users");
163 assert_eq!(diff("C:\\", "C:\\"), ".");
164 assert_eq!(diff("\\\\?\\C:\\Users\\user", "\\\\?\\C:\\Users"), "user");
165 assert_eq!(diff("\\\\?\\C:\\Users", "\\\\?\\C:\\Users"), ".");
166 assert_eq!(diff("\\\\?\\C:\\Users", "\\\\?\\C:\\"), "Users");
167 assert_eq!(diff("\\\\?\\C:\\", "\\\\?\\C:\\"), ".");
168 assert_eq!(
169 diff("\\\\?\\ASDF\\Users\\user", "\\\\?\\ASDF\\Users"),
170 "user"
171 );
172 assert_eq!(diff("\\\\?\\ASDF\\Users", "\\\\?\\ASDF\\Users"), ".");
173 assert_eq!(diff("\\\\?\\ASDF\\Users", "\\\\?\\ASDF"), "Users");
174 assert_eq!(diff("\\\\?\\ASDF", "\\\\?\\ASDF"), ".");
175 }
176}