drmem_api/types/device/
name.rs1use crate::{types::Error, Result};
13use serde_derive::Deserialize;
14use std::fmt;
15use std::str::FromStr;
16
17#[derive(Debug, Clone, PartialEq, Deserialize, Hash, Eq)]
18struct Segment(String);
19
20impl Segment {
21 fn is_valid_char((idx, ch): (usize, char), len: usize) -> bool {
25 ch.is_alphanumeric() || (ch == '-' && idx != 0 && idx != len - 1)
26 }
27
28 fn create(s: &str) -> Result<Self> {
32 if !s.is_empty() {
33 if s.chars()
34 .enumerate()
35 .all(|v| Segment::is_valid_char(v, s.len()))
36 {
37 Ok(Segment(String::from(s)))
38 } else {
39 Err(Error::InvArgument(String::from(
40 "segment contains invalid character",
41 )))
42 }
43 } else {
44 Err(Error::InvArgument(String::from(
45 "contains zero-length segment",
46 )))
47 }
48 }
49}
50
51impl FromStr for Segment {
54 type Err = Error;
55
56 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
57 Segment::create(s)
58 }
59}
60
61impl fmt::Display for Segment {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 write!(f, "{}", &self.0)
64 }
65}
66
67#[derive(Debug, PartialEq, Clone, Deserialize, Hash, Eq)]
72#[serde(try_from = "String")]
73pub struct Path(Vec<Segment>);
74
75impl Path {
76 pub fn create(s: &str) -> Result<Self> {
79 s.split(':')
80 .map(Segment::create)
81 .collect::<Result<Vec<Segment>>>()
82 .map(Path)
83 }
84}
85
86impl TryFrom<String> for Path {
92 type Error = Error;
93
94 fn try_from(s: String) -> Result<Self> {
95 Path::create(&s)
96 }
97}
98
99impl FromStr for Path {
102 type Err = Error;
103
104 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
105 Path::create(s)
106 }
107}
108
109impl fmt::Display for Path {
110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111 write!(f, "{}", &self.0[0])?;
112 for ii in &self.0[1..] {
113 write!(f, ":{}", &ii)?
114 }
115 Ok(())
116 }
117}
118
119#[derive(Debug, PartialEq, Clone, Deserialize, Hash, Eq)]
124#[serde(try_from = "String")]
125pub struct Base(Segment);
126
127impl Base {
128 pub fn create(s: &str) -> Result<Self> {
131 Segment::create(s).map(Base)
132 }
133}
134
135impl TryFrom<String> for Base {
136 type Error = Error;
137
138 fn try_from(s: String) -> Result<Self> {
139 Base::create(&s)
140 }
141}
142
143impl FromStr for Base {
146 type Err = Error;
147
148 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
149 Base::create(s)
150 }
151}
152
153impl fmt::Display for Base {
154 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155 write!(f, "{}", &self.0)
156 }
157}
158
159#[derive(Debug, PartialEq, Hash, Eq, Clone, Deserialize)]
181#[serde(try_from = "String")]
182pub struct Name {
183 path: Path,
184 base: Base,
185}
186
187impl Name {
188 pub fn create(s: &str) -> Result<Name> {
191 match s
192 .split(':')
193 .map(Segment::create)
194 .collect::<Result<Vec<Segment>>>()
195 {
196 Ok(segments) if segments.len() < 2 => Err(Error::InvArgument(
197 String::from("device name requires a path and base name"),
198 )),
199 Ok(segments) => Ok(Name {
200 path: Path(segments[0..segments.len() - 1].to_vec()),
201 base: Base(segments[segments.len() - 1].clone()),
202 }),
203 Err(e) => Err(e),
204 }
205 }
206
207 pub fn build(path: Path, base: Base) -> Name {
209 Name { path, base }
210 }
211
212 pub fn get_path(&self) -> Path {
214 self.path.clone()
215 }
216
217 pub fn get_name(&self) -> Base {
219 self.base.clone()
220 }
221}
222
223impl fmt::Display for Name {
224 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225 write!(f, "{}:{}", &self.path, &self.base)
226 }
227}
228
229impl TryFrom<String> for Name {
230 type Error = Error;
231
232 fn try_from(s: String) -> Result<Self> {
233 Name::create(&s)
234 }
235}
236
237impl FromStr for Name {
240 type Err = Error;
241
242 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
243 Name::create(s)
244 }
245}
246
247#[cfg(test)]
248mod tests {
249 use super::*;
250
251 #[test]
252 fn test_segment() {
253 assert!("".parse::<Segment>().is_err());
254 assert!(
255 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
256 .parse::<Segment>()
257 .is_ok()
258 );
259 assert!("a-b".parse::<Segment>().is_ok());
260 assert!("a:b".parse::<Segment>().is_err());
261 assert!("-a".parse::<Segment>().is_err());
262 assert!("a-".parse::<Segment>().is_err());
263 assert!(" ".parse::<Segment>().is_err());
264 assert_eq!(format!("{}", "a-b".parse::<Segment>().unwrap()), "a-b");
265
266 assert!("٣".parse::<Segment>().is_ok());
269 assert!("温度".parse::<Segment>().is_ok());
270 assert!("🤖".parse::<Segment>().is_err());
271 }
272
273 #[test]
274 fn test_base() {
275 assert_eq!(format!("{}", "a-b".parse::<Base>().unwrap()), "a-b");
276 assert!("a:b".parse::<Base>().is_err());
277 }
278
279 #[test]
280 fn test_path() {
281 assert!("".parse::<Path>().is_err());
282 assert!("basement:🤖".parse::<Path>().is_err());
283
284 assert_eq!(format!("{}", "a-b".parse::<Path>().unwrap()), "a-b");
285 assert_eq!(format!("{}", "a:b".parse::<Path>().unwrap()), "a:b");
286 assert_eq!(format!("{}", "a:b:c".parse::<Path>().unwrap()), "a:b:c");
287 assert_eq!(
288 format!("{}", "家:温度".parse::<Path>().unwrap()),
289 "家:温度"
290 );
291 }
292
293 #[test]
294 fn test_device_name() {
295 assert!("".parse::<Name>().is_err());
296 assert!(":".parse::<Name>().is_err());
297 assert!("a".parse::<Name>().is_err());
298 assert!(":a".parse::<Name>().is_err());
299 assert!("a:".parse::<Name>().is_err());
300 assert!("a::a".parse::<Name>().is_err());
301
302 assert!("p:a.".parse::<Name>().is_err());
303 assert!("p:a.a".parse::<Name>().is_err());
304 assert!("p.a:a".parse::<Name>().is_err());
305 assert!("p:a-".parse::<Name>().is_err());
306 assert!("p:-a".parse::<Name>().is_err());
307 assert!("p-:a".parse::<Name>().is_err());
308 assert!("-p:a".parse::<Name>().is_err());
309
310 assert_eq!(
311 "p:abc".parse::<Name>().unwrap(),
312 Name {
313 path: Path::create("p").unwrap(),
314 base: Base::create("abc").unwrap(),
315 }
316 );
317 assert_eq!(
318 "p:abc1".parse::<Name>().unwrap(),
319 Name {
320 path: Path::create("p").unwrap(),
321 base: Base::create("abc1").unwrap(),
322 }
323 );
324 assert_eq!(
325 "p:abc-1".parse::<Name>().unwrap(),
326 Name {
327 path: Path::create("p").unwrap(),
328 base: Base::create("abc-1").unwrap(),
329 }
330 );
331 assert_eq!(
332 "p-1:p-2:abc".parse::<Name>().unwrap(),
333 Name {
334 path: Path::create("p-1:p-2").unwrap(),
335 base: Base::create("abc").unwrap(),
336 }
337 );
338
339 let dn = "p-1:p-2:abc".parse::<Name>().unwrap();
340
341 assert_eq!(dn.get_path(), Path::create("p-1:p-2").unwrap());
342 assert_eq!(dn.get_name(), Base::create("abc").unwrap());
343
344 assert_eq!(format!("{}", dn), "p-1:p-2:abc");
345 }
346}