rosrust/api/naming/
path.rs

1use super::error::{Error, ErrorKind};
2use error_chain::bail;
3use std::fmt;
4use std::ops::{Add, AddAssign};
5
6pub trait Path {
7    fn get(&self) -> &[String];
8
9    fn take(self) -> Buffer;
10
11    fn slice(&self) -> Slice {
12        Slice { chain: self.get() }
13    }
14
15    fn parent(&self) -> Result<Slice, Error> {
16        match self.get().split_last() {
17            Some(v) => Ok(Slice { chain: v.1 }),
18            None => Err(ErrorKind::MissingParent.into()),
19        }
20    }
21}
22
23#[derive(Clone, Debug)]
24pub struct Slice<'a> {
25    chain: &'a [String],
26}
27
28impl<'a> fmt::Display for Slice<'a> {
29    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
30        for item in self.get() {
31            write!(f, "/{}", item)?;
32        }
33        Ok(())
34    }
35}
36
37impl<'a> Path for Slice<'a> {
38    fn get(&self) -> &[String] {
39        self.chain
40    }
41
42    fn take(self) -> Buffer {
43        Buffer {
44            chain: Vec::from(self.chain),
45        }
46    }
47}
48
49impl<'a, T: Path> Add<T> for Slice<'a> {
50    type Output = Buffer;
51
52    fn add(self, rhs: T) -> Self::Output {
53        self.take() + rhs
54    }
55}
56
57#[derive(Clone, Debug)]
58pub struct Buffer {
59    chain: Vec<String>,
60}
61
62impl fmt::Display for Buffer {
63    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
64        for item in self.get() {
65            write!(f, "/{}", item)?;
66        }
67        Ok(())
68    }
69}
70
71impl Path for Buffer {
72    fn get(&self) -> &[String] {
73        &self.chain
74    }
75
76    fn take(self) -> Buffer {
77        self
78    }
79}
80
81impl<T: Path> Add<T> for Buffer {
82    type Output = Buffer;
83
84    fn add(mut self, rhs: T) -> Self::Output {
85        self += rhs;
86        self
87    }
88}
89
90impl<T: Path> AddAssign<T> for Buffer {
91    fn add_assign(&mut self, rhs: T) {
92        self.chain.extend_from_slice(rhs.get());
93    }
94}
95
96impl std::str::FromStr for Buffer {
97    type Err = Error;
98
99    fn from_str(s: &str) -> Result<Self, Self::Err> {
100        let s = s.trim_end_matches('/');
101        let words = if let Some(first_char) = s.bytes().next() {
102            if !is_legal_first_char(first_char) {
103                bail!(ErrorKind::IllegalFirstCharacter(s.into()));
104            }
105            let mut word_iter = s.split('/');
106            match word_iter.next() {
107                Some("") => {}
108                Some(_) | None => bail!(ErrorKind::LeadingSlashMissing(s.into())),
109            }
110            word_iter
111                .map(process_name)
112                .collect::<Result<Vec<String>, Error>>()?
113        } else {
114            Vec::new()
115        };
116        Ok(Buffer { chain: words })
117    }
118}
119
120fn process_name(name: &str) -> Result<String, Error> {
121    if name.is_empty() {
122        bail!(ErrorKind::EmptyName)
123    }
124
125    let mut bytes = name.bytes();
126    if !bytes.all(is_legal_char) {
127        bail!(ErrorKind::IllegalCharacter(name.into()));
128    }
129    Ok(String::from(name))
130}
131
132fn is_legal_first_char(v: u8) -> bool {
133    v.is_ascii_uppercase() || v.is_ascii_lowercase() || v == b'/' || v == b'~' || v.is_ascii_digit()
134}
135
136fn is_legal_char(v: u8) -> bool {
137    is_legal_first_char(v) || v == b'_'
138}
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143
144    static FAILED_TO_HANDLE: &str = "Failed to handle";
145
146    #[test]
147    fn names_are_legal() {
148        assert!("/foo".parse::<Buffer>().is_ok());
149        assert!("/foo/bar".parse::<Buffer>().is_ok());
150        assert!("/f1_aA/Ba02/Xx".parse::<Buffer>().is_ok());
151        assert!("/asdf/".parse::<Buffer>().is_ok());
152        assert!("/".parse::<Buffer>().is_ok());
153        assert!("".parse::<Buffer>().is_ok());
154        assert!("/123/".parse::<Buffer>().is_ok());
155        assert!("/_e".parse::<Buffer>().is_ok());
156
157        assert!("a".parse::<Buffer>().is_err());
158        assert!("/foo$".parse::<Buffer>().is_err());
159        assert!("/a//b".parse::<Buffer>().is_err());
160    }
161
162    #[test]
163    fn names_are_parsed() {
164        assert_eq!(
165            vec![String::from("foo")],
166            "/foo".parse::<Buffer>().expect(FAILED_TO_HANDLE).get()
167        );
168        assert_eq!(
169            vec![String::from("foo"), String::from("bar")],
170            "/foo/bar".parse::<Buffer>().expect(FAILED_TO_HANDLE).get()
171        );
172        assert_eq!(
173            vec![
174                String::from("f1_aA"),
175                String::from("Ba02"),
176                String::from("Xx"),
177            ],
178            "/f1_aA/Ba02/Xx"
179                .parse::<Buffer>()
180                .expect(FAILED_TO_HANDLE)
181                .get()
182        );
183    }
184
185    #[test]
186    fn is_formatted() {
187        assert_eq!(
188            "/foo",
189            format!("{}", "/foo".parse::<Buffer>().expect(FAILED_TO_HANDLE))
190        );
191        assert_eq!(
192            "/foo/bar",
193            format!("{}", "/foo/bar".parse::<Buffer>().expect(FAILED_TO_HANDLE))
194        );
195        assert_eq!(
196            "/f1_aA/Ba02/Xx",
197            format!(
198                "{}",
199                "/f1_aA/Ba02/Xx".parse::<Buffer>().expect(FAILED_TO_HANDLE)
200            )
201        );
202    }
203
204    #[test]
205    fn parents_are_handled() {
206        assert_eq!(
207            "",
208            format!(
209                "{}",
210                "/foo"
211                    .parse::<Buffer>()
212                    .expect(FAILED_TO_HANDLE)
213                    .parent()
214                    .expect(FAILED_TO_HANDLE)
215            )
216        );
217        assert_eq!(
218            "/foo",
219            format!(
220                "{}",
221                "/foo/bar"
222                    .parse::<Buffer>()
223                    .expect(FAILED_TO_HANDLE)
224                    .parent()
225                    .expect(FAILED_TO_HANDLE)
226            )
227        );
228        assert_eq!(
229            "/f1_aA/Ba02",
230            format!(
231                "{}",
232                "/f1_aA/Ba02/Xx"
233                    .parse::<Buffer>()
234                    .expect(FAILED_TO_HANDLE)
235                    .parent()
236                    .expect(FAILED_TO_HANDLE)
237            )
238        );
239        assert!("/"
240            .parse::<Buffer>()
241            .expect(FAILED_TO_HANDLE)
242            .parent()
243            .is_err());
244        assert!("/foo"
245            .parse::<Buffer>()
246            .expect(FAILED_TO_HANDLE)
247            .parent()
248            .expect(FAILED_TO_HANDLE)
249            .parent()
250            .is_err());
251    }
252
253    #[test]
254    fn addition_works() {
255        let part1 = "/foo".parse::<Buffer>().expect(FAILED_TO_HANDLE);
256        let part2 = "/B4r/x".parse::<Buffer>().expect(FAILED_TO_HANDLE);
257        let part3 = "/bA_z".parse::<Buffer>().expect(FAILED_TO_HANDLE);
258        assert_eq!("/foo/B4r/x", format!("{}", part1.slice() + part2.slice()));
259        assert_eq!(
260            "/B4r/x/foo/foo",
261            format!("{}", part2.slice() + part1.slice() + part1.slice())
262        );
263        assert_eq!("/foo/B4r/x/bA_z", format!("{}", part1 + part2 + part3));
264    }
265}