1
2use dev_prefix::*;
3use types::*;
4
5use ui::types::*;
6
7lazy_static!{
11 pub static ref VALID_SEARCH_FIELDS: HashSet<String> = HashSet::from_iter(
12 ["N", "D", "P", "O", "L", "R", "T", "A",
13 "name", "def", "parts", "partof", "loc", "recurse", "text", "all"]
14 .iter().map(|s| s.to_string()));
15}
16
17impl FromStr for SearchSettings {
18 type Err = Error;
19
20 fn from_str(s: &str) -> Result<SearchSettings> {
21 let s = s.replace(' ', "");
22 if s.is_empty() {
23 let set = SearchSettings {
24 use_regex: true,
25 name: true,
26 text: true,
27 ..SearchSettings::default()
28 };
29 debug!("Using default search pattern: {:?}", set);
30 return Ok(set);
31 }
32
33 let first_char = s.chars().next().unwrap();
34 let pattern: HashSet<String> = match first_char {
35 'a'...'z' => s.split(',').map(|s| s.to_string()).collect(),
36 _ => s.chars().map(|c| c.to_string()).collect(),
37 };
38
39 debug!("Got search pattern: {:?}", pattern);
40 let invalid: HashSet<String> = pattern.difference(&VALID_SEARCH_FIELDS).cloned().collect();
41 if !invalid.is_empty() {
42 return Err(
43 ErrorKind::CmdError(format!("Unknown search fields in pattern: {:?}", invalid))
44 .into(),
45 );
46 }
47 let pc = |c| pattern.contains(c);
48 let mut set = SearchSettings {
49 use_regex: true,
50 name: pc("N") || pc("name"),
51 def: pc("D") || pc("def"),
52 parts: pc("P") || pc("parts"),
53 partof: pc("O") || pc("partof"),
54 loc: pc("L") || pc("loc"),
55 text: pc("T") || pc("text"),
56 ..SearchSettings::default()
57 };
58 if pc("A") || pc("all") {
59 set.name = !set.name;
60 set.def = !set.def;
61 set.parts = !set.parts;
62 set.partof = !set.partof;
63 set.loc = !set.loc;
64 set.text = !set.text;
65 }
66 Ok(set)
67 }
68}
69
70fn matches_name(pat: &Regex, names: &Names) -> bool {
71 for n in names.iter() {
72 if pat.is_match(&n.raw) {
73 return true;
74 }
75 }
76 false
77}
78
79pub fn show_artifact(
81 name: &Name,
82 art: &Artifact,
83 pat_case: &Regex,
84 search_settings: &SearchSettings,
85) -> bool {
86 let ss = search_settings;
87 let completed = (art.completed * 100.0).round() as i8;
88 let tested = (art.tested * 100.0).round() as i8;
89 if (ss.completed.lt && completed > ss.completed.perc) ||
90 (!ss.completed.lt && completed < ss.completed.perc) ||
91 (ss.tested.lt && tested > ss.tested.perc) ||
92 (!ss.tested.lt && tested < ss.tested.perc)
93 {
94 false
95 } else {
96 !ss.use_regex || (ss.name && pat_case.is_match(&name.raw)) ||
97 (ss.parts && matches_name(pat_case, &art.parts)) ||
98 (ss.partof && matches_name(pat_case, &art.partof)) ||
99 (ss.loc && match art.done {
100 Done::Code(ref l) => pat_case.is_match(l.path.to_string_lossy().as_ref()),
101 Done::Defined(ref s) => pat_case.is_match(s),
102 Done::NotDone => false,
103 }) || (ss.text && pat_case.is_match(&art.text))
104 }
105}
106
107#[test]
108fn test_search_settings() {
109 assert_eq!(
110 SearchSettings::from_str("NT").unwrap(),
111 SearchSettings::from_str("").unwrap()
112 );
113
114 let fs = |s| SearchSettings::from_str(s).unwrap();
115 assert_eq!(
116 fs("NT"),
117 SearchSettings {
118 use_regex: true,
119 name: true,
120 text: true,
121 ..SearchSettings::default()
122 }
123 );
124 assert_eq!(fs("NDPL"), fs("name, def, parts, loc"));
125
126 {
128 let mut set = SearchSettings::default();
129 set.use_regex = true;
130 set.name = true;
131 assert_eq!(set, fs("N"));
132 set.parts = true;
133 assert_eq!(set, fs("NP"));
134 set.def = true;
135 assert_eq!(set, fs("NPD"));
136 set.partof = true;
137 assert_eq!(set, fs("NPDO"));
138 set.text = true;
139 assert_eq!(set, fs("NPDOT"));
140 set.loc = true;
141 assert_eq!(set, fs("NPDOTL"));
142 }
143
144 assert!(SearchSettings::from_str("foobar").is_err());
145}
146
147#[test]
148fn test_show_artifact() {
149 let mut req_one = Artifact::from_str(
150 "[REQ-one]
151 partof = 'REQ-base'
152 text = 'hello bob'",
153 ).unwrap();
154 let req_two = Artifact::from_str("[REQ-two]\ntext = 'goodbye joe'").unwrap();
155 req_one.1.tested = 0.2;
156 req_one.1.completed = 0.8;
157
158 let search_bob = &Regex::new("bob").unwrap();
159 let search_two = &Regex::new("two").unwrap();
160
161 let mut settings_little_tested = SearchSettings::default();
163 settings_little_tested.tested = PercentSearch {
164 lt: false,
165 perc: 10,
166 };
167 assert!(show_artifact(
168 &req_one.0,
169 &req_one.1,
170 &search_bob,
171 &settings_little_tested,
172 ));
173
174 let mut settings_ct = SearchSettings::default();
175 settings_ct.completed = PercentSearch {
176 lt: false,
177 perc: 50,
178 };
179 settings_ct.tested = PercentSearch {
180 lt: false,
181 perc: 50,
182 };
183 assert!(!show_artifact(
184 &req_one.0,
185 &req_one.1,
186 &search_bob,
187 &settings_ct,
188 ));
189
190 let settings_name = SearchSettings::from_str("N").unwrap();
192 let settings_text = SearchSettings::from_str("T").unwrap();
193 let settings_nt = SearchSettings::from_str("NT").unwrap();
194
195 assert!(show_artifact(
196 &req_one.0,
197 &req_one.1,
198 &search_bob,
199 &settings_text,
200 ));
201 assert!(show_artifact(
202 &req_one.0,
203 &req_one.1,
204 &search_bob,
205 &settings_nt,
206 ));
207 assert!(show_artifact(
208 &req_two.0,
209 &req_two.1,
210 &search_two,
211 &settings_nt,
212 ));
213
214 assert!(!show_artifact(
215 &req_one.0,
216 &req_one.1,
217 &search_bob,
218 &settings_name,
219 ));
220 assert!(!show_artifact(
221 &req_one.0,
222 &req_one.1,
223 &search_two,
224 &settings_nt,
225 ));
226}