nu_path/form.rs
1use std::ffi::OsStr;
2
3mod private {
4 use std::ffi::OsStr;
5
6 // This trait should not be extended by external crates in order to uphold safety guarantees.
7 // As such, this trait is put inside a private module to prevent external impls.
8 // This ensures that all possible [`PathForm`]s can only be defined here and will:
9 // - be zero sized (enforced anyways by the `repr(transparent)` on `Path`)
10 // - have a no-op [`Drop`] implementation
11 pub trait Sealed: 'static {
12 fn invariants_satisfied<P: AsRef<OsStr> + ?Sized>(path: &P) -> bool;
13 }
14}
15
16/// A marker trait for the different kinds of path forms.
17/// Each form has its own invariants that are guaranteed to be upheld.
18/// The list of path forms are:
19/// - [`Any`]: a path with no invariants. It may be a relative or an absolute path.
20/// - [`Relative`]: a strictly relative path.
21/// - [`Absolute`]: a strictly absolute path.
22/// - [`Canonical`]: a path that must be in canonicalized form.
23pub trait PathForm: private::Sealed {}
24impl PathForm for Any {}
25impl PathForm for Relative {}
26impl PathForm for Absolute {}
27impl PathForm for Canonical {}
28
29/// A path whose form is unknown. It could be a relative, absolute, or canonical path.
30///
31/// The path is not guaranteed to be normalized. It may contain unresolved symlinks,
32/// trailing slashes, dot components (`..` or `.`), and repeated path separators.
33pub struct Any;
34
35impl private::Sealed for Any {
36 fn invariants_satisfied<P: AsRef<OsStr> + ?Sized>(_: &P) -> bool {
37 true
38 }
39}
40
41/// A strictly relative path.
42///
43/// The path is not guaranteed to be normalized. It may contain unresolved symlinks,
44/// trailing slashes, dot components (`..` or `.`), and repeated path separators.
45pub struct Relative;
46
47impl private::Sealed for Relative {
48 fn invariants_satisfied<P: AsRef<OsStr> + ?Sized>(path: &P) -> bool {
49 std::path::Path::new(path).is_relative()
50 }
51}
52
53/// An absolute path.
54///
55/// The path is not guaranteed to be normalized. It may contain unresolved symlinks,
56/// trailing slashes, dot components (`..` or `.`), and repeated path separators.
57pub struct Absolute;
58
59impl private::Sealed for Absolute {
60 fn invariants_satisfied<P: AsRef<OsStr> + ?Sized>(path: &P) -> bool {
61 std::path::Path::new(path).is_absolute()
62 }
63}
64
65// A canonical path.
66//
67// An absolute path with all intermediate components normalized and symbolic links resolved.
68pub struct Canonical;
69
70impl private::Sealed for Canonical {
71 fn invariants_satisfied<P: AsRef<OsStr> + ?Sized>(_: &P) -> bool {
72 true
73 }
74}
75
76/// A marker trait for [`PathForm`]s that may be relative paths.
77/// This includes only the [`Any`] and [`Relative`] path forms.
78pub trait MaybeRelative: PathForm {}
79impl MaybeRelative for Any {}
80impl MaybeRelative for Relative {}
81
82/// A marker trait for [`PathForm`]s that may be absolute paths.
83/// This includes the [`Any`], [`Absolute`], and [`Canonical`] path forms.
84pub trait MaybeAbsolute: PathForm {}
85impl MaybeAbsolute for Any {}
86impl MaybeAbsolute for Absolute {}
87impl MaybeAbsolute for Canonical {}
88
89/// A marker trait for [`PathForm`]s that are absolute paths.
90/// This includes only the [`Absolute`] and [`Canonical`] path forms.
91///
92/// Only [`PathForm`]s that implement this trait can be easily converted to [`std::path::Path`]
93/// or [`std::path::PathBuf`]. This is to encourage/force other Nushell crates to account for
94/// the emulated current working directory, instead of using the [`std::env::current_dir`].
95pub trait IsAbsolute: PathForm {}
96impl IsAbsolute for Absolute {}
97impl IsAbsolute for Canonical {}
98
99/// A marker trait that signifies one [`PathForm`] can be used as or trivially converted to
100/// another [`PathForm`].
101///
102/// The list of possible conversions are:
103/// - [`Relative`], [`Absolute`], or [`Canonical`] into [`Any`].
104/// - [`Canonical`] into [`Absolute`].
105/// - Any form into itself.
106pub trait PathCast<Form: PathForm>: PathForm {}
107impl<Form: PathForm> PathCast<Form> for Form {}
108impl PathCast<Any> for Relative {}
109impl PathCast<Any> for Absolute {}
110impl PathCast<Any> for Canonical {}
111impl PathCast<Absolute> for Canonical {}
112
113/// A trait used to specify the output [`PathForm`] of a path join operation.
114///
115/// The output path forms based on the left hand side path form are as follows:
116///
117/// | Left hand side | Output form |
118/// | --------------:|:------------ |
119/// | [`Any`] | [`Any`] |
120/// | [`Relative`] | [`Any`] |
121/// | [`Absolute`] | [`Absolute`] |
122/// | [`Canonical`] | [`Absolute`] |
123pub trait PathJoin: PathForm {
124 type Output: PathForm;
125}
126impl PathJoin for Any {
127 type Output = Self;
128}
129impl PathJoin for Relative {
130 type Output = Any;
131}
132impl PathJoin for Absolute {
133 type Output = Self;
134}
135impl PathJoin for Canonical {
136 type Output = Absolute;
137}
138
139/// A marker trait for [`PathForm`]s that support setting the file name or extension.
140///
141/// This includes the [`Any`], [`Relative`], and [`Absolute`] path forms.
142/// [`Canonical`] paths do not support this, since appending file names and extensions that contain
143/// path separators can cause the path to no longer be canonical.
144pub trait PathSet: PathForm {}
145impl PathSet for Any {}
146impl PathSet for Relative {}
147impl PathSet for Absolute {}
148
149/// A marker trait for [`PathForm`]s that support pushing paths.
150///
151/// This includes only [`Any`] and [`Absolute`] path forms.
152/// Pushing onto a [`Relative`] path could cause it to become [`Absolute`],
153/// which is why they do not support pushing.
154/// In the future, a `push_rel` and/or a `try_push` method could be added as an alternative.
155/// Similarly, [`Canonical`] paths may become uncanonical if a path is pushed onto it.
156pub trait PathPush: PathSet {}
157impl PathPush for Any {}
158impl PathPush for Absolute {}