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
use std::borrow::Cow;
use std::iter::repeat;
pub fn is_abs_path(s: &str) -> bool {
if s.starts_with('/') || s.starts_with('\\') {
return true;
} else if s.len() > 3 {
let b = s.as_bytes();
if b[1] == b':'
&& (b[2] == b'/' || b[2] == b'\\')
&& ((b[0] >= b'a' && b[0] <= b'z') || (b[0] >= b'A' && b[0] <= b'Z'))
{
return true;
}
}
false
}
fn get_common_prefix_len<'a>(items: &'a [Cow<'a, [&'a str]>]) -> usize {
if items.is_empty() {
return 0;
}
let shortest = &items[0];
let mut max_idx = None;
for seq in items.iter() {
let mut seq_max_idx = None;
for (idx, &comp) in shortest.iter().enumerate() {
if seq.get(idx) != Some(&comp) {
break;
}
seq_max_idx = Some(idx);
}
if max_idx.is_none() || seq_max_idx < max_idx {
max_idx = seq_max_idx;
}
}
if let Some(max_idx) = max_idx {
max_idx + 1
} else {
0
}
}
fn chunk_path(p: &str) -> Vec<&str> {
return p
.split(&['/', '\\'][..])
.filter(|x| !x.is_empty() && *x != ".")
.collect();
}
pub fn make_relative_path(base: &str, target: &str) -> String {
let mut target_str = target;
{
let target_lower = target.to_ascii_lowercase();
if target_lower.strip_prefix("file://").is_some() {
target_str = &target[7..];
}
}
if !is_abs_path(target_str) {
if target_str.contains(':') {
String::from(target_str)
} else {
return chunk_path(target_str).join("/");
}
} else {
let target_path: Vec<&str> = chunk_path(target_str);
let base_dir: Vec<&str> = chunk_path(base);
let items = vec![
Cow::Borrowed(base_dir.as_slice()),
Cow::Borrowed(target_path.as_slice()),
];
let prefix_len = get_common_prefix_len(&items);
let mut rel_list: Vec<&str> = repeat("..").take(base_dir.len() - prefix_len).collect();
rel_list.extend_from_slice(&target_path[prefix_len..]);
rel_list.join("/")
}
}
#[test]
fn test_make_relative_path() {
assert_eq!(
&make_relative_path("/foo/bar", "/foo/bar/baz.map"),
"baz.map"
);
assert_eq!(
&make_relative_path("/foo/bar/.", "/foo/bar/baz.map"),
"baz.map"
);
assert_eq!(
&make_relative_path("/foo/bar", "/foo/baz.map"),
"../baz.map"
);
assert_eq!(&make_relative_path("/some/abs/path", "foo.js"), "foo.js");
assert_eq!(
&make_relative_path("C:\\blah\\sub", "C:\\blah\\foo.js"),
"../foo.js"
);
assert_eq!(&make_relative_path("/", "./test.js"), "test.js");
}