File size: 1,592 Bytes
afa0cbf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
//! Paths resolved using the app-server host's platform rules.

use std::fmt;

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AppServerPath(String);

impl AppServerPath {
    pub fn from_app_server(path: impl Into<String>) -> Self {
        Self(path.into())
    }

    pub fn from_absolute_str(raw: &str) -> Option<Self> {
        (raw.starts_with('/') || is_windows_absolute_path(raw)).then(|| Self(raw.to_string()))
    }

    pub fn as_str(&self) -> &str {
        &self.0
    }

    pub fn components(&self) -> Vec<&str> {
        let separators = if is_windows_absolute_path(&self.0) {
            &['/', '\\'][..]
        } else {
            &['/'][..]
        };
        self.0
            .split(separators)
            .filter(|part| !part.is_empty())
            .collect()
    }

    pub fn join(&self, segment: impl AsRef<str>) -> Self {
        let is_windows = is_windows_absolute_path(&self.0);
        let (path, separator) = if is_windows {
            (self.0.trim_end_matches(['/', '\\']), '\\')
        } else {
            (self.0.trim_end_matches('/'), '/')
        };
        Self(format!("{path}{separator}{}", segment.as_ref()))
    }
}

impl fmt::Display for AppServerPath {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.0.fmt(f)
    }
}

fn is_windows_absolute_path(path: &str) -> bool {
    let bytes = path.as_bytes();
    (bytes.len() >= 3
        && bytes[0].is_ascii_alphabetic()
        && bytes[1] == b':'
        && matches!(bytes[2], b'\\' | b'/'))
        || path.starts_with("\\\\")
        || path.starts_with("//")
}