ella_engine/
path.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
use std::{fmt::Display, path::PathBuf, str::FromStr};

use object_store::path::Path as ObjPath;
use url::Url;

#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Path(Url);

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

impl AsRef<str> for Path {
    fn as_ref(&self) -> &str {
        self.0.as_ref()
    }
}

impl AsRef<Url> for Path {
    fn as_ref(&self) -> &Url {
        &self.0
    }
}

impl FromStr for Path {
    type Err = crate::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if let Ok(url) = Url::parse(s) {
            Ok(Self(url))
        } else {
            let mut path: PathBuf = s.parse().expect("path parsing is infallible");

            // Can't use fs::canonicalize if path doesn't exist yet
            if path.exists() {
                path = path.canonicalize()?;
            } else if !path.has_root() {
                path = std::env::current_dir()?.join(path);
            }

            match Url::from_file_path(path) {
                Ok(url) => Ok(Self(url)),
                Err(_) => Err(crate::EngineError::InvalidFilename(format!(
                    "\"{s}\" is not a valid URL or local filepath"
                ))
                .into()),
            }
        }
    }
}

impl TryFrom<&str> for Path {
    type Error = crate::Error;

    fn try_from(value: &str) -> Result<Self, Self::Error> {
        Ok(Self(Url::try_from(value)?))
    }
}

impl Path {
    pub(crate) fn as_path(&self) -> ObjPath {
        ObjPath::from(self.0.path())
    }

    pub fn join(&self, input: &str) -> Self {
        let mut out = self.clone();
        out.0.path_segments_mut().unwrap().push(input);
        out
    }

    pub fn filename(&self) -> Option<&str> {
        self.0.path_segments().and_then(|p| p.last())
    }

    pub(crate) fn store_url(&self) -> Self {
        let mut this = self.clone();
        if let Ok(mut path) = this.0.path_segments_mut() {
            path.clear();
        }
        this
    }
}