postcard_bindgen_core/
path.rs

1use core::fmt::Display;
2use std::borrow::Cow;
3
4use genco::{lang::Lang, tokens::FormatInto};
5
6/// A part of a path which can either be [str] or [String].
7pub type Part<'a> = Cow<'a, str>;
8/// A full path joined which can either be [str] or [String].
9pub type FullPath<'a> = Cow<'a, str>;
10
11/// A path buffer that can be used to build paths.
12///
13/// The difference between a `PathBuf` and a `Path` is that a `PathBuf` is mutable and can be
14/// modified, while a `Path` is immutable and is joined into one but knows how to split it into
15/// parts.
16///
17/// # Example
18///
19/// ```rust
20/// use postcard_bindgen_core::path::PathBuf;
21///
22/// let mut path = PathBuf::new();
23///
24/// assert!(path.is_empty());
25///
26/// path.push("foo");
27/// path.push("bar");
28/// path.push("baz");
29///
30/// assert!(!path.is_empty());
31/// assert_eq!(path.parts().map(|p| p.as_ref()).collect::<Vec<&str>>(), vec!["foo", "bar", "baz"]);
32/// ```
33#[derive(Debug, Hash, Eq, PartialEq, Clone, PartialOrd, Ord)]
34pub struct PathBuf<'a> {
35    parts: Vec<Part<'a>>,
36}
37
38impl Default for PathBuf<'_> {
39    fn default() -> Self {
40        Self::new()
41    }
42}
43
44impl<'a> PathBuf<'a> {
45    /// Create a new empty path buffer.
46    pub fn new() -> Self {
47        PathBuf { parts: Vec::new() }
48    }
49
50    /// Join a part into the path buffer by consuming the [PathBuf].
51    ///
52    /// This allows to chain calls to join parts into the path buffer.
53    ///
54    /// # Example
55    ///
56    /// ```rust
57    /// use postcard_bindgen_core::path::PathBuf;
58    ///
59    /// let path = PathBuf::new().join("foo").join("bar").join("baz");
60    ///
61    /// assert_eq!(path.parts().map(|p| p.as_ref()).collect::<Vec<&str>>(), vec!["foo", "bar", "baz"]);
62    /// ```
63    pub fn join(mut self, joiner: impl Into<Part<'a>>) -> Self {
64        self.parts.push(joiner.into());
65        self
66    }
67
68    /// Push a part to the end of the path buffer.
69    pub fn push(&mut self, part: impl Into<Part<'a>>) {
70        self.parts.push(part.into());
71    }
72
73    /// Push a part to the front of the path buffer.
74    pub fn push_front(&mut self, part: impl Into<Part<'a>>) {
75        self.parts.insert(0, part.into());
76    }
77
78    /// Pop a part from the end of the path buffer.
79    pub fn pop_front(&mut self) {
80        if !self.parts.is_empty() {
81            let _ = self.parts.remove(0);
82        }
83    }
84
85    /// Gives an iterator over the parts of the path buffer.
86    pub fn parts(&self) -> impl Iterator<Item = &Part<'a>> {
87        self.parts.iter()
88    }
89
90    /// Check if the path buffer is empty.
91    pub fn is_empty(&self) -> bool {
92        self.parts.is_empty()
93    }
94
95    /// Convert the path buffer into a [Path] by consuming the path buffer.
96    pub fn into_path<'b>(self, joiner: impl Into<Part<'b>>) -> Path<'a, 'b> {
97        let joiner = joiner.into();
98        Path {
99            path: Some(self.parts.join(joiner.as_ref()).into()),
100            joiner,
101        }
102    }
103
104    /// Flatten the path to the root by simply removing all parts.
105    pub fn flatten(&mut self) {
106        self.parts.clear();
107    }
108
109    /// Convert the path buffer into an owned path buffer by consuming the path buffer.
110    pub fn into_owned(self) -> PathBuf<'static> {
111        PathBuf {
112            parts: self
113                .parts
114                .into_iter()
115                .map(|p| p.into_owned().into())
116                .collect(),
117        }
118    }
119}
120
121impl<'a> FromIterator<Part<'a>> for PathBuf<'a> {
122    fn from_iter<I>(iter: I) -> Self
123    where
124        I: IntoIterator<Item = Part<'a>>,
125    {
126        PathBuf {
127            parts: iter.into_iter().collect(),
128        }
129    }
130}
131
132impl<'a> From<&'a str> for PathBuf<'a> {
133    fn from(value: &'a str) -> Self {
134        PathBuf {
135            parts: vec![Cow::Borrowed(value)],
136        }
137    }
138}
139
140/// A [Path] can be used to represent a path.
141///
142/// A path is a sequence of parts that are joined together by a joiner. The joiner is a string that
143/// is used to split the path into parts and into a [PathBuf].
144///
145/// # Example
146///
147/// ```rust
148/// use postcard_bindgen_core::path::Path;
149///
150/// let path = Path::new("foo/bar/baz", "/");
151///
152/// assert_eq!(path.parts().collect::<Vec<&str>>(), vec!["foo", "bar", "baz"]);
153#[derive(Debug, Clone, PartialEq, Eq)]
154pub struct Path<'a, 'b> {
155    path: Option<FullPath<'a>>,
156    joiner: Cow<'b, str>,
157}
158
159impl<'a, 'b> Path<'a, 'b> {
160    /// Create a new path from something which can be converted into a [FullPath] and a joiner
161    /// which is either [str] or [String].
162    pub fn new(path: impl Into<FullPath<'a>>, joiner: impl Into<Cow<'b, str>>) -> Self {
163        Path {
164            path: Some(path.into()),
165            joiner: joiner.into(),
166        }
167    }
168
169    /// Gives an iterator over the parts of the path.
170    pub fn parts(&self) -> impl Iterator<Item = &str> {
171        self.path
172            .as_ref()
173            .map(|p| p.split(self.joiner.as_ref()))
174            .into_iter()
175            .flatten()
176    }
177
178    /// Flatten the path to the root by simply removing all parts.
179    pub fn flatten(&mut self) {
180        self.path = None;
181    }
182
183    /// Convert the path into a [PathBuf] by consuming the path and splitting it into parts.
184    pub fn into_buf(self) -> PathBuf<'a> {
185        PathBuf {
186            parts: self
187                .path
188                .map(|p| {
189                    p.split(self.joiner.as_ref())
190                        .map(|p| p.to_owned().into())
191                        .collect::<Vec<Part<'a>>>()
192                })
193                .unwrap_or_default(),
194        }
195    }
196
197    /// Check if the path is empty.
198    pub fn is_empty(&self) -> bool {
199        self.path.as_ref().map(|p| p.is_empty()).unwrap_or(true)
200    }
201
202    /// Convert the path into an owned path by consuming the path.
203    pub fn into_owned(self) -> Path<'static, 'static> {
204        Path {
205            joiner: self.joiner.into_owned().into(),
206            path: self.path.map(|p| p.into_owned().into()),
207        }
208    }
209}
210
211impl<'a, 'b> From<Path<'a, 'b>> for String {
212    fn from(value: Path<'a, 'b>) -> Self {
213        value.path.map(|c| c.into_owned()).unwrap_or_default()
214    }
215}
216
217impl Display for Path<'_, '_> {
218    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
219        if let Some(path) = &self.path {
220            write!(f, "{}", path)
221        } else {
222            write!(f, "")
223        }
224    }
225}
226
227impl<L: Lang> FormatInto<L> for Path<'_, '_> {
228    fn format_into(self, t: &mut genco::tokens::Tokens<L>) {
229        if let Some(path) = &self.path {
230            path.format_into(t);
231        }
232    }
233}