artifact_app/ui/
search.rs

1
2use dev_prefix::*;
3use types::*;
4
5use ui::types::*;
6
7//#[cfg(test)]
8//use user::types::*;
9
10lazy_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
79/// return true if the artifact meets the criteria
80pub 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    // build it up one at a time
127    {
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    // test percentage search
162    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    // test regex search
191    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}