artifact_app/user/
name.rs1use std::hash::{Hash, Hasher};
11use std::cmp::Ordering;
12
13use dev_prefix::*;
14use types::*;
15
16impl FromStr for Name {
19 type Err = Error;
20 fn from_str(s: &str) -> Result<Name> {
21 Name::from_string(s.to_string())
22 }
23}
24
25impl Name {
26 pub fn from_string(s: String) -> Result<Name> {
27 let value = s.to_ascii_uppercase();
28 if !NAME_VALID.is_match(&value) {
29 return Err(ErrorKind::InvalidName(s.to_string()).into());
30 }
31 let value: Vec<String> = value.split('-').map(|s| s.to_string()).collect();
32 let ty = _get_type(&value[0], &s)?;
33 Ok(Name {
34 raw: s,
35 value: value,
36 ty: ty,
37 })
38 }
39
40 pub fn parent(&self) -> Option<Name> {
44 if self.value.len() <= 2 {
45 return None;
46 }
47 let mut value = self.value.clone();
48 value.pop().unwrap();
49 Some(Name {
50 raw: value.join("-"),
51 value: value,
52 ty: self.ty,
53 })
54 }
55
56 pub fn is_root(&self) -> bool {
58 self.value.len() == 2
59 }
60
61 pub fn parent_rc(&self) -> Option<NameRc> {
62 match self.parent() {
63 Some(p) => Some(Arc::new(p)),
64 None => None,
65 }
66 }
67
68 pub fn named_partofs(&self) -> Vec<Name> {
72 match self.ty {
73 Type::TST => vec![self._get_named_partof("SPC")],
74 Type::SPC => vec![self._get_named_partof("REQ")],
75 Type::REQ => vec![],
76 }
77 }
78
79 fn _get_named_partof(&self, ty: &str) -> Name {
81 let s = ty.to_string() + self.raw.split_at(3).1;
82 Name::from_str(&s).unwrap()
83 }
84}
85
86#[test]
87fn test_artname_parent() {
88 let name = Name::from_str("REQ-foo-bar-b").unwrap();
89 let parent = name.parent().unwrap();
90 assert_eq!(parent, Name::from_str("REQ-foo-bar").unwrap());
91 let parent = parent.parent().unwrap();
92 assert_eq!(parent, Name::from_str("REQ-foo").unwrap());
93}
94
95impl Default for Name {
96 fn default() -> Name {
97 Name::from_str("REQ-default").unwrap()
98 }
99}
100
101impl fmt::Display for Name {
102 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
103 write!(f, "{}", self.raw)
104 }
105}
106
107impl fmt::Debug for Name {
108 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
109 write!(f, "{}", self.value.join("-"))
110 }
111}
112
113impl Hash for Name {
114 fn hash<H: Hasher>(&self, state: &mut H) {
115 self.value.hash(state);
116 }
117}
118
119impl PartialEq for Name {
120 fn eq(&self, other: &Name) -> bool {
121 self.value == other.value
122 }
123}
124
125impl Eq for Name {}
126
127impl Ord for Name {
128 fn cmp(&self, other: &Self) -> Ordering {
129 self.value.cmp(&other.value)
130 }
131}
132
133impl PartialOrd for Name {
134 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
135 self.value.partial_cmp(&other.value)
136 }
137}
138
139impl LoadFromStr for NameRc {
140 fn from_str(s: &str) -> Result<NameRc> {
141 Ok(Arc::new(try!(Name::from_str(s))))
142 }
143}
144
145impl LoadFromStr for Names {
146 fn from_str(partof_str: &str) -> Result<Names> {
148 let strs = try!(parse_names(&mut partof_str.chars(), false));
149 let mut out = HashSet::new();
150 for s in strs {
151 out.insert(Arc::new(try!(Name::from_str(&s))));
152 }
153 Ok(out)
154 }
155}
156
157fn _get_type(value: &str, raw: &str) -> Result<Type> {
160 match value {
161 "REQ" => Ok(Type::REQ),
162 "SPC" => Ok(Type::SPC),
163 "TST" => Ok(Type::TST),
164 _ => Err(
165 ErrorKind::InvalidName(format!(
166 "name must start with REQ-, SPC- or TST-: \
167 {}",
168 raw
169 )).into(),
170 ),
171 }
172}
173
174fn parse_names<I>(raw: &mut I, in_brackets: bool) -> Result<Vec<String>>
178where
179 I: Iterator<Item = char>,
180{
181 let mut strout = String::new();
184 let mut current = String::new();
185 loop {
186 let c = match raw.next() {
188 Some(c) => c,
189 None => {
190 if in_brackets {
191 return Err(
193 ErrorKind::InvalidName("brackets are not closed".to_string()).into(),
194 );
195 }
196 break;
197 }
198 };
199 match c {
200 ' ' | '\n' | '\r' => {}
201 '[' => {
203 if current == "" {
204 let msg = "cannot have '[' after characters ',' or ']'\
206 or at start of string"
207 .to_string();
208 return Err(ErrorKind::InvalidName(msg).into());
209 }
210 for p in try!(parse_names(raw, true)) {
212 strout.write_str(¤t).unwrap();
213 strout.write_str(&p).unwrap();
214 strout.push(',');
215 }
216 current.clear();
217 }
218 ']' => break,
219 ',' => {
220 strout.write_str(¤t).unwrap();
221 strout.push(',');
222 current.clear();
223 }
224 _ => current.push(c),
225 }
226 }
227 strout.write_str(¤t).unwrap();
228 Ok(
229 strout
230 .split(',')
231 .filter(|s| s != &"")
232 .map(|s| s.to_string())
233 .collect(),
234 )
235}
236
237#[cfg(test)]
238fn do_test_parse(user: &str, expected_collapsed: &[&str]) {
239 let parsed = parse_names(&mut user.chars(), false).unwrap();
240 assert_eq!(parsed, expected_collapsed);
241}
242
243#[test]
244fn test_parse_names() {
246 do_test_parse("hi, ho", &["hi", "ho"]);
247 do_test_parse("hi-[he, ho]", &["hi-he", "hi-ho"]);
248 do_test_parse(
249 "he-[ha-[ha, he], hi, ho], hi-[he, ho]",
250 &["he-ha-ha", "he-ha-he", "he-hi", "he-ho", "hi-he", "hi-ho"],
251 );
252 assert!(parse_names(&mut "[]".chars(), false).is_err());
253 assert!(parse_names(&mut "[hi]".chars(), false).is_err());
254 assert!(parse_names(&mut "hi-[ho, [he]]".chars(), false).is_err());
255 assert!(parse_names(&mut "hi-[ho, he".chars(), false).is_err());
256}
257
258#[test]
259fn test_name() {
260 for name in vec![
262 "REQ-foo",
263 "REQ-foo-2",
264 "REQ-foo2",
265 "REQ-foo2",
266 "REQ-foo-bar-2_3",
267 "SPC-foo",
268 "TST-foo",
269 ] {
270 assert!(Name::from_str(name).is_ok());
271 }
272 for name in vec!["REQ-foo*", "REQ-foo\n", "REQ-foo-"] {
273 assert!(Name::from_str(name).is_err())
274 }
275 assert!(Name::from_str("REQ-foo ").is_err());
277}