faster_path 0.3.10 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/src/basename.rs CHANGED
@@ -1,146 +1,243 @@
1
- extern crate memchr;
2
-
3
- use self::memchr::memrchr;
4
-
5
- use path_parsing::{find_last_sep_pos, find_last_non_sep_pos};
6
-
7
- pub fn basename<'a>(path: &'a str, ext: &str) -> &'a str {
8
- let bytes: &[u8] = path.as_bytes();
9
- let mut left: usize = 0;
10
- let mut right: usize = bytes.len();
11
- if let Some(last_slash_pos) = find_last_sep_pos(bytes) {
12
- if last_slash_pos == right - 1 {
13
- if let Some(pos) = find_last_non_sep_pos(&bytes[..last_slash_pos]) {
14
- right = pos + 1;
15
- } else {
16
- return "/";
17
- }
18
- if let Some(pos) = find_last_sep_pos(&bytes[..right]) {
19
- left = pos + 1;
20
- }
21
- } else {
22
- left = last_slash_pos + 1;
23
- }
1
+ use memchr::memrchr;
2
+
3
+ use crate::path_parsing::{Rules, SEP_BYTES};
4
+
5
+ // Ruby's `File.basename(path, ext)`.
6
+ pub fn basename<'a>(rules: Rules, path: &'a [u8], ext: &[u8]) -> &'a [u8] {
7
+ let (name, base_len) = last_component(rules, path);
8
+ match base_len {
9
+ Some(base_len) => &name[..ext_end(rules, name, base_len, ext)],
10
+ None => name,
24
11
  }
25
- &path[left..left + ext_end(&bytes[left..right], ext)]
26
12
  }
27
13
 
28
- fn ext_end(slice: &[u8], ext: &str) -> usize {
29
- if ext.len() >= slice.len() || slice == b"." || slice == b".." {
30
- return slice.len();
14
+ // Ruby's `ruby_enc_find_basename`: the last component of the path, without
15
+ // trailing separators (and on Windows, trailing dots, spaces and `:stream`),
16
+ // and where that component ends without its extension. A path that is only
17
+ // separators and prefixes has no extension: its "component" is a separator,
18
+ // or "" for a drive letter.
19
+ pub fn last_component(rules: Rules, path: &[u8]) -> (&[u8], Option<usize>) {
20
+ if path.is_empty() {
21
+ return (path, None);
31
22
  }
32
- let ext_bytes = ext.as_bytes();
33
- if ext_bytes.len() == 2 && *ext_bytes.get(1).unwrap() == b'*' {
34
- match memrchr(*ext_bytes.get(0).unwrap(), slice) {
35
- Some(end) if end != 0 => return end,
36
- _ => {}
23
+ if !rules.is_dosish() {
24
+ // On Unix this is just the last name, or the last separator.
25
+ let end = match rules.last_non_sep_pos(path) {
26
+ Some(pos) => pos + 1,
27
+ None => return (&path[path.len() - 1..], None),
37
28
  };
38
- } else if slice.ends_with(ext_bytes) {
39
- return slice.len() - ext_bytes.len();
29
+ // Paths are short: a plain scan beats `memrchr`'s setup here.
30
+ let start = path[..end].iter().rposition(|&c| c == b'/').map_or(0, |pos| pos + 1);
31
+ let component = &path[start..end];
32
+ return (component, Some(base_len(component)));
33
+ }
34
+ let root = rules.skip_prefix(path);
35
+ let mut name = root;
36
+ while name < path.len() && rules.is_sep(path[name]) {
37
+ name += 1;
38
+ }
39
+ if name == path.len() {
40
+ if name != root {
41
+ // The last separator
42
+ return (&path[name - 1..name], None);
43
+ }
44
+ if path[name - 1] == b':' {
45
+ // A drive letter
46
+ return (&path[name..], None);
47
+ }
48
+ // A UNC prefix
49
+ return (SEP_BYTES, None);
40
50
  }
41
- slice.len()
51
+ let start = match rules.last_separator(&path[name..]) {
52
+ Some(pos) => {
53
+ let mut start = name + pos;
54
+ while rules.is_sep(path[start]) {
55
+ start += 1;
56
+ }
57
+ start
58
+ }
59
+ None => name,
60
+ };
61
+ let rest = &path[start..];
62
+ let component = &rest[..rules.ntfs_tail(rest)];
63
+ (component, Some(base_len(component)))
64
+ }
65
+
66
+ // Where the name ends without its extension: at its last dot, unless that
67
+ // is one of its leading dots.
68
+ fn base_len(component: &[u8]) -> usize {
69
+ let dots = component.iter().take_while(|&&c| c == b'.').count();
70
+ component[dots..].iter().rposition(|&c| c == b'.').map_or(component.len(), |pos| dots + pos)
71
+ }
72
+
73
+ // Ruby's `rmext`: where `component` ends without the extension `ext`, or
74
+ // its length if `ext` isn't there. `ext` is an extension to remove, ".*"
75
+ // for any extension, or a character followed by `*` to remove everything
76
+ // from the last occurrence of that character.
77
+ pub fn ext_end(rules: Rules, component: &[u8], base_len: usize, ext: &[u8]) -> usize {
78
+ let len = component.len();
79
+ let end = match *ext {
80
+ [] => 0,
81
+ [b'.', b'*'] => base_len,
82
+ [c, b'*'] => memrchr(c, component).unwrap_or(len),
83
+ _ if len < ext.len() => len,
84
+ _ => {
85
+ let start = len - ext.len();
86
+ if rules.same_path(&component[start..], ext) { start } else { 0 }
87
+ }
88
+ };
89
+ if end == 0 { len } else { end }
90
+ }
91
+
92
+ #[cfg(test)]
93
+ fn basename_str<'a>(path: &'a str, ext: &str) -> &'a str {
94
+ std::str::from_utf8(basename(Rules::UNIX, path.as_bytes(), ext.as_bytes())).unwrap()
95
+ }
96
+
97
+ #[cfg(test)]
98
+ fn windows_basename<'a>(path: &'a str, ext: &str) -> &'a str {
99
+ std::str::from_utf8(basename(Rules::WINDOWS, path.as_bytes(), ext.as_bytes())).unwrap()
42
100
  }
43
101
 
44
102
  #[test]
45
103
  fn non_dot_asterisk_ext() {
46
104
  // This is undocumented Ruby functionality. We match it in case some code out there relies on it.
47
- assert_eq!(basename("abc", "b*"), "a");
48
- assert_eq!(basename("abc", "abc"), "abc");
49
- assert_eq!(basename("abc", "a*"), "abc");
50
- assert_eq!(basename("playlist", "l*"), "play");
105
+ assert_eq!(basename_str("abc", "b*"), "a");
106
+ assert_eq!(basename_str("abc", "abc"), "abc");
107
+ assert_eq!(basename_str("abc", "a*"), "abc");
108
+ assert_eq!(basename_str("playlist", "l*"), "play");
51
109
  // Treated as literal "*":
52
- assert_eq!(basename("playlist", "yl*"), "playlist");
53
- assert_eq!(basename("playl*", "yl*"), "pla");
110
+ assert_eq!(basename_str("playlist", "yl*"), "playlist");
111
+ assert_eq!(basename_str("playl*", "yl*"), "pla");
54
112
  }
55
113
 
56
114
  #[test]
57
115
  fn empty() {
58
- assert_eq!(basename("", ""), "");
59
- assert_eq!(basename("", ".*"), "");
60
- assert_eq!(basename("", ".a"), "");
116
+ assert_eq!(basename_str("", ""), "");
117
+ assert_eq!(basename_str("", ".*"), "");
118
+ assert_eq!(basename_str("", ".a"), "");
61
119
  }
62
120
 
63
121
  #[test]
64
122
  fn sep() {
65
- assert_eq!(basename("/", ""), "/");
66
- assert_eq!(basename("//", ""), "/");
123
+ assert_eq!(basename_str("/", ""), "/");
124
+ assert_eq!(basename_str("//", ""), "/");
67
125
  }
68
126
 
69
127
  #[test]
70
128
  fn trailing_dot() {
71
- assert_eq!(basename("file.test.", ""), "file.test.");
72
- assert_eq!(basename("file.test.", "."), "file.test");
73
- assert_eq!(basename("file.test.", ".*"), "file.test");
129
+ assert_eq!(basename_str("file.test.", ""), "file.test.");
130
+ assert_eq!(basename_str("file.test.", "."), "file.test");
131
+ assert_eq!(basename_str("file.test.", ".*"), "file.test");
74
132
  }
75
133
 
76
134
  #[test]
77
135
  fn trailing_dot_dot() {
78
- assert_eq!(basename("a..", ".."), "a");
79
- assert_eq!(basename("a..", ".*"), "a.");
136
+ assert_eq!(basename_str("a..", ".."), "a");
137
+ assert_eq!(basename_str("a..", ".*"), "a.");
80
138
  }
81
139
 
82
140
  #[test]
83
141
  fn dot() {
84
- assert_eq!(basename(".", ""), ".");
85
- assert_eq!(basename(".", "."), ".");
86
- assert_eq!(basename(".", ".*"), ".");
142
+ assert_eq!(basename_str(".", ""), ".");
143
+ assert_eq!(basename_str(".", "."), ".");
144
+ assert_eq!(basename_str(".", ".*"), ".");
87
145
  }
88
146
 
89
147
  #[test]
90
148
  fn dot_dot() {
91
- assert_eq!(basename("..", ""), "..");
92
- assert_eq!(basename("..", ".*"), "..");
93
- assert_eq!(basename("..", ".."), "..");
94
- assert_eq!(basename("..", "..."), "..");
149
+ assert_eq!(basename_str("..", ""), "..");
150
+ assert_eq!(basename_str("..", ".*"), "..");
151
+ assert_eq!(basename_str("..", ".."), "..");
152
+ assert_eq!(basename_str("..", "..."), "..");
95
153
  }
96
154
 
97
155
  #[test]
98
156
  fn non_dot_ext() {
99
- assert_eq!(basename("abc", "bc"), "a");
157
+ assert_eq!(basename_str("abc", "bc"), "a");
100
158
  }
101
159
 
102
160
  #[test]
103
161
  fn basename_eq_ext() {
104
- assert_eq!(basename(".x", ".x"), ".x");
105
- assert_eq!(basename(".x", ".*"), ".x");
162
+ assert_eq!(basename_str(".x", ".x"), ".x");
163
+ assert_eq!(basename_str(".x", ".*"), ".x");
106
164
  }
107
165
 
108
166
  #[test]
109
167
  fn absolute() {
110
- assert_eq!(basename("/a/b///c", ""), "c");
168
+ assert_eq!(basename_str("/a/b///c", ""), "c");
111
169
  }
112
170
 
113
171
  #[test]
114
172
  fn trailing_slashes_absolute() {
115
- assert_eq!(basename("/a/b///c//////", ""), "c");
173
+ assert_eq!(basename_str("/a/b///c//////", ""), "c");
116
174
  }
117
175
 
118
176
  #[test]
119
177
  fn relative() {
120
- assert_eq!(basename("b///c", ""), "c");
178
+ assert_eq!(basename_str("b///c", ""), "c");
121
179
  }
122
180
 
123
181
  #[test]
124
182
  fn trailing_slashes_relative() {
125
- assert_eq!(basename("b/c//", ""), "c");
183
+ assert_eq!(basename_str("b/c//", ""), "c");
126
184
  }
127
185
 
128
186
  #[test]
129
187
  fn root() {
130
- assert_eq!(basename("//c", ""), "c");
188
+ assert_eq!(basename_str("//c", ""), "c");
131
189
  }
132
190
 
133
191
  #[test]
134
192
  fn trailing_slashes_root() {
135
- assert_eq!(basename("//c//", ""), "c");
193
+ assert_eq!(basename_str("//c//", ""), "c");
136
194
  }
137
195
 
138
196
  #[test]
139
197
  fn trailing_slashes_relative_root() {
140
- assert_eq!(basename("c//", ""), "c");
198
+ assert_eq!(basename_str("c//", ""), "c");
141
199
  }
142
200
 
143
201
  #[test]
144
202
  fn edge_case_all_seps() {
145
- assert_eq!("/", basename("///", ".*"));
203
+ assert_eq!("/", basename_str("///", ".*"));
204
+ }
205
+
206
+ #[test]
207
+ fn ext_compared_like_ruby() {
208
+ assert_eq!(basename_str("..", "."), ".");
209
+ assert_eq!(basename_str("a.rb", ".RB"), "a.rb");
210
+ assert_eq!(windows_basename("a.rb", ".RB"), "a");
211
+ }
212
+
213
+ #[test]
214
+ fn windows_separators() {
215
+ assert_eq!(windows_basename("a\\b", ""), "b");
216
+ assert_eq!(windows_basename("a\\b\\", ""), "b");
217
+ assert_eq!(windows_basename("a/b\\c", ""), "c");
218
+ assert_eq!(windows_basename("\\", ""), "\\");
219
+ assert_eq!(basename_str("a\\b", ""), "a\\b");
220
+ }
221
+
222
+ #[test]
223
+ fn windows_prefixes() {
224
+ assert_eq!(windows_basename("C:", ""), "");
225
+ assert_eq!(windows_basename("C:/", ""), "/");
226
+ assert_eq!(windows_basename("C:/a", ""), "a");
227
+ assert_eq!(windows_basename("C:a", ""), "a");
228
+ assert_eq!(windows_basename("//a/b", ""), "/");
229
+ assert_eq!(windows_basename("//a/b/", ""), "/");
230
+ assert_eq!(windows_basename("//a/b/c", ""), "c");
231
+ assert_eq!(basename_str("//a/b", ""), "b");
232
+ }
233
+
234
+ #[test]
235
+ fn windows_ntfs_names() {
236
+ assert_eq!(windows_basename("foo.test ", ""), "foo.test");
237
+ assert_eq!(windows_basename("foo.test.", ""), "foo.test");
238
+ assert_eq!(windows_basename("foo.test::$DATA", ""), "foo.test");
239
+ assert_eq!(windows_basename("foo.test ", ".test"), "foo");
240
+ assert_eq!(windows_basename("foo.test.", ".*"), "foo");
241
+ assert_eq!(windows_basename("foo.test::$DATA", ".*"), "foo");
242
+ assert_eq!(windows_basename("...", ""), "...");
146
243
  }
data/src/chop_basename.rs CHANGED
@@ -1,30 +1,93 @@
1
- use path_parsing::{find_last_non_sep_pos, find_last_sep_pos};
2
- use std::str;
3
-
4
- pub fn chop_basename<'a>(input: &'a str) -> Option<(&'a str, &'a str)> {
5
- let bytes = input.as_bytes();
6
- let len = find_last_non_sep_pos(&bytes)? + 1;
7
- let base_start = find_last_sep_pos(&bytes[..len]).map_or(0, |pos| pos + 1);
8
- if base_start == len {
1
+ use memchr::memmem;
2
+
3
+ use crate::basename::basename;
4
+ use crate::path_parsing::{offset_in, Rules};
5
+
6
+ // Pathname's `chop_basename`: the path split before its basename, unless
7
+ // the basename is empty or a separator.
8
+ pub fn chop_basename(rules: Rules, path: &[u8]) -> Option<(&[u8], &[u8])> {
9
+ let base = basename(rules, path, b"");
10
+ if base.is_empty() || (base.len() == 1 && rules.is_sep(base[0])) {
9
11
  return None;
10
12
  }
11
- Some((&input[0..base_start], &input[base_start..len]))
13
+ // `path.rindex(base)`. On Unix, nothing after the basename can contain it.
14
+ let start = if rules.is_dosish() { memmem::rfind(path, base)? } else { offset_in(path, base) };
15
+ Some((&path[..start], &path[start..start + base.len()]))
16
+ }
17
+
18
+ // Pathname's `relative?`: whether chopping every basename off leaves nothing.
19
+ pub fn is_relative(rules: Rules, path: &[u8]) -> bool {
20
+ if !rules.is_dosish() {
21
+ return path.first() != Some(&b'/');
22
+ }
23
+ let mut prefix = path;
24
+ while let Some((rest, _)) = chop_basename(rules, prefix) {
25
+ prefix = rest;
26
+ }
27
+ prefix.is_empty()
28
+ }
29
+
30
+ #[cfg(test)]
31
+ fn chop_basename_str(input: &str) -> Option<(&str, &str)> {
32
+ chop_basename(Rules::UNIX, input.as_bytes()).map(|(dirname, basename)| {
33
+ (std::str::from_utf8(dirname).unwrap(), std::str::from_utf8(basename).unwrap())
34
+ })
12
35
  }
13
36
 
14
37
  #[test]
15
38
  fn it_chops_the_basename_and_dirname() {
16
- assert_eq!(chop_basename(""), None );
17
- assert_eq!(chop_basename("/"), None );
18
- assert_eq!(chop_basename("."), Some(("", ".")) );
19
- assert_eq!(chop_basename("asdf/asdf"), Some(("asdf/", "asdf")) );
20
- assert_eq!(chop_basename("asdf.txt"), Some(("", "asdf.txt")) );
21
- assert_eq!(chop_basename("asdf/"), Some(("", "asdf")) );
22
- assert_eq!(chop_basename("/asdf/"), Some(("/", "asdf")) );
23
- assert_eq!(chop_basename("a///b"), Some(("a///", "b")) );
24
- assert_eq!(chop_basename("a///b//"), Some(("a///", "b")) );
25
- assert_eq!(chop_basename("/a///b//"), Some(("/a///", "b")) );
26
- assert_eq!(chop_basename("/a///b//"), Some(("/a///", "b")) );
39
+ assert_eq!(chop_basename_str(""), None );
40
+ assert_eq!(chop_basename_str("/"), None );
41
+ assert_eq!(chop_basename_str("."), Some(("", ".")) );
42
+ assert_eq!(chop_basename_str("asdf/asdf"), Some(("asdf/", "asdf")) );
43
+ assert_eq!(chop_basename_str("asdf.txt"), Some(("", "asdf.txt")) );
44
+ assert_eq!(chop_basename_str("asdf/"), Some(("", "asdf")) );
45
+ assert_eq!(chop_basename_str("/asdf/"), Some(("/", "asdf")) );
46
+ assert_eq!(chop_basename_str("a///b"), Some(("a///", "b")) );
47
+ assert_eq!(chop_basename_str("a///b//"), Some(("a///", "b")) );
48
+ assert_eq!(chop_basename_str("/a///b//"), Some(("/a///", "b")) );
49
+ assert_eq!(chop_basename_str("/a///b//"), Some(("/a///", "b")) );
50
+
51
+ assert_eq!(chop_basename_str("./../..///.../..//"), Some(("./../..///.../", "..")));
52
+ }
27
53
 
28
- assert_eq!(chop_basename("./../..///.../..//"), Some(("./../..///.../", "..")));
54
+ #[test]
55
+ fn it_chops_non_utf8_paths() {
56
+ assert_eq!(chop_basename(Rules::UNIX, b"\xff/\xfe\xfd/"), Some((&b"\xff/"[..], &b"\xfe\xfd"[..])));
57
+ }
58
+
59
+ #[cfg(test)]
60
+ fn windows_chop(input: &str) -> Option<(&str, &str)> {
61
+ chop_basename(Rules::WINDOWS, input.as_bytes()).map(|(dirname, basename)| {
62
+ (std::str::from_utf8(dirname).unwrap(), std::str::from_utf8(basename).unwrap())
63
+ })
64
+ }
65
+
66
+ #[test]
67
+ fn it_chops_windows_paths() {
68
+ assert_eq!(windows_chop("a\\b"), Some(("a\\", "b")));
69
+ assert_eq!(windows_chop("a\\b\\"), Some(("a\\", "b")));
70
+ assert_eq!(windows_chop("C:"), None);
71
+ assert_eq!(windows_chop("C:/"), None);
72
+ assert_eq!(windows_chop("C:/a"), Some(("C:/", "a")));
73
+ assert_eq!(windows_chop("C:a"), Some(("C:", "a")));
74
+ assert_eq!(windows_chop("//a/b"), None);
75
+ assert_eq!(windows_chop("//a/b/c"), Some(("//a/b/", "c")));
76
+ assert_eq!(windows_chop("a."), Some(("", "a")));
29
77
  }
30
78
 
79
+ #[test]
80
+ fn it_knows_relative_paths() {
81
+ for &rules in &[Rules::UNIX, Rules::WINDOWS] {
82
+ assert!(is_relative(rules, b""));
83
+ assert!(is_relative(rules, b"a"));
84
+ assert!(is_relative(rules, b"a/b"));
85
+ assert!(!is_relative(rules, b"/"));
86
+ assert!(!is_relative(rules, b"/a"));
87
+ }
88
+ assert!(is_relative(Rules::UNIX, b"C:/a"));
89
+ assert!(is_relative(Rules::UNIX, b"\\a"));
90
+ for path in &["A:", "A:/", "A:/a", "//", "//a", "//a/", "//a/b", "//a/b/", "//a/b/c", "\\a"] {
91
+ assert!(!is_relative(Rules::WINDOWS, path.as_bytes()), "{}", path);
92
+ }
93
+ }
@@ -1,19 +1,21 @@
1
1
  use std::borrow::Cow;
2
- use prepend_prefix::prepend_prefix;
3
- use basename::basename;
4
- use chop_basename::chop_basename;
5
- use path_parsing::{SEP_STR, contains_sep};
6
2
 
7
- pub fn cleanpath_aggressive(path: &str) -> Cow<str> {
8
- let mut names: Vec<&str> = vec![];
3
+ use crate::basename::basename;
4
+ use crate::chop_basename::chop_basename;
5
+ use crate::path_parsing::{Rules, SEP_BYTES};
6
+ use crate::prepend_prefix::prepend_prefix;
7
+
8
+ // Pathname's `cleanpath_aggressive`
9
+ pub fn cleanpath_aggressive(rules: Rules, path: &[u8]) -> Cow<'_, [u8]> {
10
+ let mut names: Vec<&[u8]> = vec![];
9
11
  let mut prefix = path;
10
- while let Some((ref p, ref base)) = chop_basename(&prefix) {
12
+ while let Some((p, base)) = chop_basename(rules, prefix) {
11
13
  prefix = p;
12
- match base.as_ref() {
13
- "." => {}
14
- ".." => names.push(base),
14
+ match base {
15
+ b"." => {}
16
+ b".." => names.push(base),
15
17
  _ => {
16
- if names.last() == Some(&"..") {
18
+ if names.last() == Some(&&b".."[..]) {
17
19
  names.pop();
18
20
  } else {
19
21
  names.push(base);
@@ -21,72 +23,86 @@ pub fn cleanpath_aggressive(path: &str) -> Cow<str> {
21
23
  }
22
24
  }
23
25
  }
24
- // // Windows Feature
25
- //
26
- // ```ruby
27
- // pre.tr!(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
28
- // ```
29
- //
30
- if contains_sep(basename(&prefix, "").as_bytes()) {
31
- let len = names.iter().rposition(|&c| c != "..").map_or(0, |pos| pos + 1);
26
+ let prefix = with_separators(rules, prefix);
27
+ if rules.contains_sep(basename(rules, &prefix, b"")) {
28
+ let len = names.iter().rposition(|&c| c != b"..").map_or(0, |pos| pos + 1);
32
29
  names.truncate(len);
33
30
  }
34
31
  names.reverse();
35
- prepend_prefix(&prefix, &names.join(&SEP_STR))
32
+ let path = prepend_prefix(rules, &prefix, &names.join(SEP_BYTES));
33
+ Cow::Owned(path.into_owned())
34
+ }
35
+
36
+ // `pre.tr!(File::ALT_SEPARATOR, File::SEPARATOR)`
37
+ pub fn with_separators(rules: Rules, prefix: &[u8]) -> Cow<'_, [u8]> {
38
+ if rules.is_dosish() && prefix.contains(&b'\\') {
39
+ Cow::Owned(prefix.iter().map(|&c| if c == b'\\' { b'/' } else { c }).collect())
40
+ } else {
41
+ Cow::Borrowed(prefix)
42
+ }
43
+ }
44
+
45
+ #[cfg(test)]
46
+ fn cleanpath_aggressive_str(path: &str) -> String {
47
+ String::from_utf8(cleanpath_aggressive(Rules::UNIX, path.as_bytes()).into_owned()).unwrap()
48
+ }
49
+
50
+ #[cfg(test)]
51
+ fn windows_cleanpath_aggressive(path: &str) -> String {
52
+ String::from_utf8(cleanpath_aggressive(Rules::WINDOWS, path.as_bytes()).into_owned()).unwrap()
36
53
  }
37
54
 
38
55
  #[test]
39
56
  fn it_aggressively_cleans_the_path() {
40
- assert_eq!(cleanpath_aggressive("/") , "/");
41
- assert_eq!(cleanpath_aggressive("") , ".");
42
- assert_eq!(cleanpath_aggressive(".") , ".");
43
- assert_eq!(cleanpath_aggressive("..") , "..");
44
- assert_eq!(cleanpath_aggressive("a") , "a");
45
- assert_eq!(cleanpath_aggressive("/.") , "/");
46
- assert_eq!(cleanpath_aggressive("/..") , "/");
47
- assert_eq!(cleanpath_aggressive("/a") , "/a");
48
- assert_eq!(cleanpath_aggressive("./") , ".");
49
- assert_eq!(cleanpath_aggressive("../") , "..");
50
- assert_eq!(cleanpath_aggressive("a/") , "a");
51
- assert_eq!(cleanpath_aggressive("a//b") , "a/b");
52
- assert_eq!(cleanpath_aggressive("a/.") , "a");
53
- assert_eq!(cleanpath_aggressive("a/./") , "a");
54
- assert_eq!(cleanpath_aggressive("a/..") , ".");
55
- assert_eq!(cleanpath_aggressive("a/../") , ".");
56
- assert_eq!(cleanpath_aggressive("/a/.") , "/a");
57
- assert_eq!(cleanpath_aggressive("./..") , "..");
58
- assert_eq!(cleanpath_aggressive("../.") , "..");
59
- assert_eq!(cleanpath_aggressive("./../") , "..");
60
- assert_eq!(cleanpath_aggressive(".././") , "..");
61
- assert_eq!(cleanpath_aggressive("/./..") , "/");
62
- assert_eq!(cleanpath_aggressive("/../.") , "/");
63
- assert_eq!(cleanpath_aggressive("/./../") , "/");
64
- assert_eq!(cleanpath_aggressive("/.././") , "/");
65
- assert_eq!(cleanpath_aggressive("a/b/c") , "a/b/c");
66
- assert_eq!(cleanpath_aggressive("./b/c") , "b/c");
67
- assert_eq!(cleanpath_aggressive("a/./c") , "a/c");
68
- assert_eq!(cleanpath_aggressive("a/b/.") , "a/b");
69
- assert_eq!(cleanpath_aggressive("a/../.") , ".");
70
- assert_eq!(cleanpath_aggressive("/../.././../a") , "/a");
71
- assert_eq!(cleanpath_aggressive("a/b/../../../../c/../d"), "../../d");
57
+ assert_eq!(cleanpath_aggressive_str("/") , "/");
58
+ assert_eq!(cleanpath_aggressive_str("") , ".");
59
+ assert_eq!(cleanpath_aggressive_str(".") , ".");
60
+ assert_eq!(cleanpath_aggressive_str("..") , "..");
61
+ assert_eq!(cleanpath_aggressive_str("a") , "a");
62
+ assert_eq!(cleanpath_aggressive_str("/.") , "/");
63
+ assert_eq!(cleanpath_aggressive_str("/..") , "/");
64
+ assert_eq!(cleanpath_aggressive_str("/a") , "/a");
65
+ assert_eq!(cleanpath_aggressive_str("./") , ".");
66
+ assert_eq!(cleanpath_aggressive_str("../") , "..");
67
+ assert_eq!(cleanpath_aggressive_str("a/") , "a");
68
+ assert_eq!(cleanpath_aggressive_str("a//b") , "a/b");
69
+ assert_eq!(cleanpath_aggressive_str("a/.") , "a");
70
+ assert_eq!(cleanpath_aggressive_str("a/./") , "a");
71
+ assert_eq!(cleanpath_aggressive_str("a/..") , ".");
72
+ assert_eq!(cleanpath_aggressive_str("a/../") , ".");
73
+ assert_eq!(cleanpath_aggressive_str("/a/.") , "/a");
74
+ assert_eq!(cleanpath_aggressive_str("./..") , "..");
75
+ assert_eq!(cleanpath_aggressive_str("../.") , "..");
76
+ assert_eq!(cleanpath_aggressive_str("./../") , "..");
77
+ assert_eq!(cleanpath_aggressive_str(".././") , "..");
78
+ assert_eq!(cleanpath_aggressive_str("/./..") , "/");
79
+ assert_eq!(cleanpath_aggressive_str("/../.") , "/");
80
+ assert_eq!(cleanpath_aggressive_str("/./../") , "/");
81
+ assert_eq!(cleanpath_aggressive_str("/.././") , "/");
82
+ assert_eq!(cleanpath_aggressive_str("a/b/c") , "a/b/c");
83
+ assert_eq!(cleanpath_aggressive_str("./b/c") , "b/c");
84
+ assert_eq!(cleanpath_aggressive_str("a/./c") , "a/c");
85
+ assert_eq!(cleanpath_aggressive_str("a/b/.") , "a/b");
86
+ assert_eq!(cleanpath_aggressive_str("a/../.") , ".");
87
+ assert_eq!(cleanpath_aggressive_str("/../.././../a") , "/a");
88
+ assert_eq!(cleanpath_aggressive_str("a/b/../../../../c/../d"), "../../d");
89
+ }
90
+
91
+ #[test]
92
+ fn it_cleans_like_ruby_on_unix() {
93
+ assert_eq!(cleanpath_aggressive_str("///"), "/");
94
+ assert_eq!(cleanpath_aggressive_str("///a"), "/a");
95
+ assert_eq!(cleanpath_aggressive_str("///.."), "/");
96
+ assert_eq!(cleanpath_aggressive_str("///."), "/");
97
+ assert_eq!(cleanpath_aggressive_str("///a/../.."), "/");
72
98
  }
73
99
 
74
- // Future Windows Support
75
- //
76
- // DOSISH = File::ALT_SEPARATOR != nil
77
- // DOSISH_DRIVE_LETTER = File.dirname("A:") == "A:."
78
- // DOSISH_UNC = File.dirname("//") == "//"
79
- //
80
- // if DOSISH_UNC
81
- // defassert(:cleanpath_aggressive, '//a/b/c', '//a/b/c/')
82
- // else
83
- // defassert(:cleanpath_aggressive, '/', '///')
84
- // defassert(:cleanpath_aggressive, '/a', '///a')
85
- // defassert(:cleanpath_aggressive, '/', '///..')
86
- // defassert(:cleanpath_aggressive, '/', '///.')
87
- // defassert(:cleanpath_aggressive, '/', '///a/../..')
88
- // end
89
- //
90
- // if DOSISH
91
- // defassert(:cleanpath_aggressive, 'c:/foo/bar', 'c:\\foo\\bar')
92
- // end
100
+ #[test]
101
+ fn it_cleans_like_ruby_on_windows() {
102
+ assert_eq!(windows_cleanpath_aggressive("//a/b/c/"), "//a/b/c");
103
+ assert_eq!(windows_cleanpath_aggressive("c:\\foo\\bar"), "c:/foo/bar");
104
+ assert_eq!(windows_cleanpath_aggressive("c:\\foo\\..\\..\\bar"), "c:/bar");
105
+ assert_eq!(windows_cleanpath_aggressive("a\\.\\b"), "a/b");
106
+ assert_eq!(windows_cleanpath_aggressive("C:"), "C:.");
107
+ assert_eq!(windows_cleanpath_aggressive("C:a/../b"), "C:b");
108
+ }