forked from mozilla/application-services
-
Notifications
You must be signed in to change notification settings - Fork 0
/
util.rs
133 lines (121 loc) · 4.3 KB
/
util.rs
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
129
130
131
132
133
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::error::{ErrorKind, Result};
use std::path::{Path, PathBuf};
use url::Url;
/// Equivalent to `&s[..max_len.min(s.len())]`, but handles the case where
/// `s.is_char_boundary(max_len)` is false (which would otherwise panic).
pub fn slice_up_to(s: &str, max_len: usize) -> &str {
if max_len >= s.len() {
return s;
}
let mut idx = max_len;
while !s.is_char_boundary(idx) {
idx -= 1;
}
&s[..idx]
}
/// `Path` is basically just a `str` with no validation, and so in practice it
/// could contain a file URL. Rusqlite takes advantage of this a bit, and says
/// `AsRef<Path>` but really means "anything sqlite can take as an argument".
///
/// Swift loves using file urls (the only support it has for file manipulation
/// is through file urls), so it's handy to support them if possible.
fn unurl_path(p: impl AsRef<Path>) -> PathBuf {
p.as_ref()
.to_str()
.and_then(|s| Url::parse(s).ok())
.and_then(|u| {
if u.scheme() == "file" {
u.to_file_path().ok()
} else {
None
}
})
.unwrap_or_else(|| p.as_ref().to_owned())
}
/// If `p` is a file URL, return it, otherwise try and make it one.
///
/// Errors if `p` is a relative non-url path, or if it's a URL path
/// that's isn't a `file:` URL.
pub fn ensure_url_path(p: impl AsRef<Path>) -> Result<Url> {
if let Some(u) = p.as_ref().to_str().and_then(|s| Url::parse(s).ok()) {
if u.scheme() == "file" {
Ok(u)
} else {
Err(ErrorKind::IllegalDatabasePath(p.as_ref().to_owned()).into())
}
} else {
let p = p.as_ref();
let u = Url::from_file_path(p).map_err(|_| ErrorKind::IllegalDatabasePath(p.to_owned()))?;
Ok(u)
}
}
/// As best as possible, convert `p` into an absolute path, resolving
/// all symlinks along the way.
///
/// If `p` is a file url, it's converted to a path before this.
pub fn normalize_path(p: impl AsRef<Path>) -> Result<PathBuf> {
let path = unurl_path(p);
if let Ok(canonical) = path.canonicalize() {
return Ok(canonical);
}
// It probably doesn't exist yet. This is an error, although it seems to
// work on some systems.
//
// We resolve this by trying to canonicalize the parent directory, and
// appending the requested file name onto that. If we can't canonicalize
// the parent, we return an error.
//
// Also, we return errors if the path ends in "..", if there is no
// parent directory, etc.
let file_name = path
.file_name()
.ok_or_else(|| ErrorKind::IllegalDatabasePath(path.clone()))?;
let parent = path
.parent()
.ok_or_else(|| ErrorKind::IllegalDatabasePath(path.clone()))?;
let mut canonical = parent.canonicalize()?;
canonical.push(file_name);
Ok(canonical)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_slice_up_to() {
assert_eq!(slice_up_to("abcde", 4), "abcd");
assert_eq!(slice_up_to("abcde", 5), "abcde");
assert_eq!(slice_up_to("abcde", 6), "abcde");
let s = "abcd😀";
assert_eq!(s.len(), 8);
assert_eq!(slice_up_to(s, 4), "abcd");
assert_eq!(slice_up_to(s, 5), "abcd");
assert_eq!(slice_up_to(s, 6), "abcd");
assert_eq!(slice_up_to(s, 7), "abcd");
assert_eq!(slice_up_to(s, 8), s);
}
#[test]
fn test_unurl_path() {
assert_eq!(
unurl_path("file:///foo%20bar/baz").to_string_lossy(),
"/foo bar/baz"
);
assert_eq!(unurl_path("/foo bar/baz").to_string_lossy(), "/foo bar/baz");
assert_eq!(unurl_path("../baz").to_string_lossy(), "../baz");
}
#[test]
fn test_ensure_url() {
assert_eq!(
ensure_url_path("file:///foo%20bar/baz").unwrap().as_str(),
"file:///foo%20bar/baz"
);
assert_eq!(
ensure_url_path("/foo bar/baz").unwrap().as_str(),
"file:///foo%20bar/baz"
);
assert!(ensure_url_path("bar").is_err());
assert!(ensure_url_path("http://www.not-a-file.com").is_err());
}
}