cubob/
path.rs

1use core::fmt;
2
3/// Helper type to mark something that shouldn't be outputted.
4/// Used as replacer in some instantiations of [`Path`].
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub struct NoOutput;
7
8impl fmt::Display for NoOutput {
9    fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
10        Ok(())
11    }
12}
13
14/// Special type which helps to output different path-like types.
15/// Everything that produces [`fmt::Display`] items while being iterated can be outputted this way.
16/// Implements [`crate::Params`] and intended to be used as argument of [`crate::WithParams::with_params`].
17/// ```
18/// use cubob::{PathLike, WithParams};
19/// 
20/// let path_like_1 = vec!["A", "a", "1"];
21/// let path_like_2 = [1, 2, 3, 4];
22/// 
23/// assert_eq!(path_like_1.with_params(PathLike::STRUCT).to_string(), "A.a.1");
24/// assert_eq!(path_like_1.with_params(PathLike::LIST).to_string(), "A, a, 1");
25/// assert_eq!(path_like_2.with_params(PathLike::FS_RELATIVE).to_string(), "1/2/3/4");
26/// assert_eq!(path_like_2.with_params(PathLike::ROUTE).to_string(), "1->2->3->4");
27/// 
28/// ```
29pub struct PathLike<D, R> {
30    delimiter: D,
31    replacer: R,
32    prepend: bool,
33}
34
35impl PathLike<char, NoOutput> {
36    pub const STRUCT: &'static Self = &Self {
37        delimiter: '.',
38        replacer: NoOutput,
39        prepend: false,
40    };
41}
42
43impl PathLike<char, char> {
44    pub const FS_RELATIVE: &'static Self = &Self {
45        delimiter: '/',
46        replacer: '.',
47        prepend: false,
48    };
49    pub const FS_ABSOLUTE: &'static Self = &Self {
50        delimiter: '/',
51        replacer: '.',
52        prepend: true,
53    };
54}
55
56impl PathLike<&'static str, NoOutput> {
57    pub const ROUTE: &'static Self = &Self {
58        delimiter: "->",
59        replacer: NoOutput,
60        prepend: false,
61    };
62    pub const LIST: &'static Self = &Self {
63        delimiter: ", ",
64        replacer: NoOutput,
65        prepend: false
66    };
67}
68
69impl<D> PathLike<D, NoOutput> {
70    pub fn new(delimiter: D, prepend: bool) -> Self {
71        Self { delimiter, replacer: NoOutput, prepend }
72    }
73}
74
75impl<D1, R> PathLike<D1, R> {
76    pub fn with_delimiter<D2>(self, delimiter: D2) -> PathLike<D2, R> {
77        let Self {
78            delimiter: _,
79            replacer,
80            prepend,
81        } = self;
82        PathLike {
83            delimiter,
84            replacer,
85            prepend,
86        }
87    }
88}
89
90impl<D, R1> PathLike<D, R1> {
91    pub fn with_replacer<R2>(self, replacer: R2) -> PathLike<D, R2> {
92        let Self {
93            delimiter,
94            replacer: _,
95            prepend,
96        } = self;
97        PathLike {
98            delimiter,
99            replacer,
100            prepend,
101        }
102    }
103}
104
105impl<D, R> PathLike<D, R> {
106    pub fn with_prepend(self, prepend: bool) -> Self {
107        Self { prepend, ..self }
108    }
109}
110
111impl<D, R, I> crate::Params<I> for PathLike<D, R>
112where
113    D: fmt::Display,
114    R: fmt::Display,
115    I: ?Sized,
116    for <'a> &'a I: IntoIterator<Item: fmt::Display>,
117{
118    fn fmt(&self, value: &I, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119        let mut iter = value.into_iter();
120
121        let Some(first) = iter.next() else {
122            return self.replacer.fmt(f);
123        };
124
125        match self.prepend {
126            false => fmt::Display::fmt(&first, f),
127            true => output_component(f, &self.delimiter, first),
128        }?;
129
130        while let Some(item) = iter.next() {
131            output_component(f, &self.delimiter, item)?;
132        }
133
134        Ok(())
135    }
136}
137
138
139fn output_component(
140    f: &mut fmt::Formatter<'_>,
141    delimiter: impl fmt::Display,
142    item: impl fmt::Display,
143) -> fmt::Result {
144    write!(f, "{delimiter}{item}")
145}