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}