nu_path/
form.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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
use std::ffi::OsStr;

mod private {
    use std::ffi::OsStr;

    // This trait should not be extended by external crates in order to uphold safety guarantees.
    // As such, this trait is put inside a private module to prevent external impls.
    // This ensures that all possible [`PathForm`]s can only be defined here and will:
    // - be zero sized (enforced anyways by the `repr(transparent)` on `Path`)
    // - have a no-op [`Drop`] implementation
    pub trait Sealed: 'static {
        fn invariants_satisfied<P: AsRef<OsStr> + ?Sized>(path: &P) -> bool;
    }
}

/// A marker trait for the different kinds of path forms.
/// Each form has its own invariants that are guaranteed be upheld.
/// The list of path forms are:
/// - [`Any`]: a path with no invariants. It may be a relative or an absolute path.
/// - [`Relative`]: a strictly relative path.
/// - [`Absolute`]: a strictly absolute path.
/// - [`Canonical`]: a path that must be in canonicalized form.
pub trait PathForm: private::Sealed {}
impl PathForm for Any {}
impl PathForm for Relative {}
impl PathForm for Absolute {}
impl PathForm for Canonical {}

/// A path whose form is unknown. It could be a relative, absolute, or canonical path.
///
/// The path is not guaranteed to be normalized. It may contain unresolved symlinks,
/// trailing slashes, dot components (`..` or `.`), and repeated path separators.
pub struct Any;

impl private::Sealed for Any {
    fn invariants_satisfied<P: AsRef<OsStr> + ?Sized>(_: &P) -> bool {
        true
    }
}

/// A strictly relative path.
///
/// The path is not guaranteed to be normalized. It may contain unresolved symlinks,
/// trailing slashes, dot components (`..` or `.`), and repeated path separators.
pub struct Relative;

impl private::Sealed for Relative {
    fn invariants_satisfied<P: AsRef<OsStr> + ?Sized>(path: &P) -> bool {
        std::path::Path::new(path).is_relative()
    }
}

/// An absolute path.
///
/// The path is not guaranteed to be normalized. It may contain unresolved symlinks,
/// trailing slashes, dot components (`..` or `.`), and repeated path separators.
pub struct Absolute;

impl private::Sealed for Absolute {
    fn invariants_satisfied<P: AsRef<OsStr> + ?Sized>(path: &P) -> bool {
        std::path::Path::new(path).is_absolute()
    }
}

// A canonical path.
//
// An absolute path with all intermediate components normalized and symbolic links resolved.
pub struct Canonical;

impl private::Sealed for Canonical {
    fn invariants_satisfied<P: AsRef<OsStr> + ?Sized>(_: &P) -> bool {
        true
    }
}

/// A marker trait for [`PathForm`]s that may be relative paths.
/// This includes only the [`Any`] and [`Relative`] path forms.
pub trait MaybeRelative: PathForm {}
impl MaybeRelative for Any {}
impl MaybeRelative for Relative {}

/// A marker trait for [`PathForm`]s that may be absolute paths.
/// This includes the [`Any`], [`Absolute`], and [`Canonical`] path forms.
pub trait MaybeAbsolute: PathForm {}
impl MaybeAbsolute for Any {}
impl MaybeAbsolute for Absolute {}
impl MaybeAbsolute for Canonical {}

/// A marker trait for [`PathForm`]s that are absolute paths.
/// This includes only the [`Absolute`] and [`Canonical`] path forms.
///
/// Only [`PathForm`]s that implement this trait can be easily converted to [`std::path::Path`]
/// or [`std::path::PathBuf`]. This is to encourage/force other Nushell crates to account for
/// the emulated current working directory, instead of using the [`std::env::current_dir`].
pub trait IsAbsolute: PathForm {}
impl IsAbsolute for Absolute {}
impl IsAbsolute for Canonical {}

/// A marker trait that signifies one [`PathForm`] can be used as or trivially converted to
/// another [`PathForm`].
///
/// The list of possible conversions are:
/// - [`Relative`], [`Absolute`], or [`Canonical`] into [`Any`].
/// - [`Canonical`] into [`Absolute`].
/// - Any form into itself.
pub trait PathCast<Form: PathForm>: PathForm {}
impl<Form: PathForm> PathCast<Form> for Form {}
impl PathCast<Any> for Relative {}
impl PathCast<Any> for Absolute {}
impl PathCast<Any> for Canonical {}
impl PathCast<Absolute> for Canonical {}

/// A trait used to specify the output [`PathForm`] of a path join operation.
///
/// The output path forms based on the left hand side path form are as follows:
///
/// | Left hand side | Output form  |
/// | --------------:|:------------ |
/// | [`Any`]        | [`Any`]      |
/// | [`Relative`]   | [`Any`]      |
/// | [`Absolute`]   | [`Absolute`] |
/// | [`Canonical`]  | [`Absolute`] |
pub trait PathJoin: PathForm {
    type Output: PathForm;
}
impl PathJoin for Any {
    type Output = Self;
}
impl PathJoin for Relative {
    type Output = Any;
}
impl PathJoin for Absolute {
    type Output = Self;
}
impl PathJoin for Canonical {
    type Output = Absolute;
}

/// A marker trait for [`PathForm`]s that support setting the file name or extension.
///
/// This includes the [`Any`], [`Relative`], and [`Absolute`] path forms.
/// [`Canonical`] paths do not support this, since appending file names and extensions that contain
/// path separators can cause the path to no longer be canonical.
pub trait PathSet: PathForm {}
impl PathSet for Any {}
impl PathSet for Relative {}
impl PathSet for Absolute {}

/// A marker trait for [`PathForm`]s that support pushing paths.
///
/// This includes only [`Any`] and [`Absolute`] path forms.
/// Pushing onto a [`Relative`] path could cause it to become [`Absolute`],
/// which is why they do not support pushing.
/// In the future, a `push_rel` and/or a `try_push` method could be added as an alternative.
/// Similarly, [`Canonical`] paths may become uncanonical if a path is pushed onto it.
pub trait PathPush: PathSet {}
impl PathPush for Any {}
impl PathPush for Absolute {}