artifact_app/user/
project.rs

1//! methods and functions for operating on Project structs.
2
3use dev_prefix::*;
4use types::*;
5
6// Public Traits
7
8impl Project {
9    /// better than equal... has reasons why NOT equal!
10    pub fn equal(&self, other: &Project) -> Result<()> {
11        names_equal(&self.artifacts, &other.artifacts)?;
12        attr_equal(
13            "path",
14            &self.artifacts,
15            &other.artifacts,
16            &|a: &Artifact| &a.def,
17        )?;
18        attr_equal(
19            "text",
20            &self.artifacts,
21            &other.artifacts,
22            &|a: &Artifact| &a.text,
23        )?;
24        attr_equal(
25            "partof",
26            &self.artifacts,
27            &other.artifacts,
28            &|a: &Artifact| &a.partof,
29        )?;
30        attr_equal(
31            "parts",
32            &self.artifacts,
33            &other.artifacts,
34            &|a: &Artifact| &a.parts,
35        )?;
36        attr_equal(
37            "done",
38            &self.artifacts,
39            &other.artifacts,
40            &|a: &Artifact| &a.done,
41        )?;
42        float_equal(
43            "completed",
44            &self.artifacts,
45            &other.artifacts,
46            &|a: &Artifact| a.completed,
47        )?;
48        float_equal(
49            "tested",
50            &self.artifacts,
51            &other.artifacts,
52            &|a: &Artifact| a.tested,
53        )?;
54        proj_attr_equal("origin", &self.origin, &other.origin)?;
55        proj_attr_equal("settings", &self.settings, &other.settings)?;
56        proj_attr_equal("files", &self.files, &other.files)?;
57        proj_attr_equal("dne_locs", &self.dne_locs, &other.dne_locs)?;
58        proj_attr_equal("repo_map", &self.repo_map, &other.repo_map)?;
59        Ok(())
60    }
61}
62
63// Private Methods
64
65/// assert that two artifact groups have the same name keys
66fn names_equal(a: &Artifacts, b: &Artifacts) -> Result<()> {
67    let a_keys: HashSet<NameRc> = a.keys().cloned().collect();
68    let b_keys: HashSet<NameRc> = b.keys().cloned().collect();
69    if b_keys != a_keys {
70        let missing = a_keys.symmetric_difference(&b_keys).collect::<Vec<_>>();
71        let msg = format!(
72            "missing artifacts: {:?}\nFIRST:\n{:?}\nSECOND:\n{:?}",
73            missing,
74            a_keys,
75            b_keys
76        );
77        Err(ErrorKind::NotEqual(msg).into())
78    } else {
79        Ok(())
80    }
81}
82
83/// assert that the attributes are equal on the artifact
84/// if they are not, then find what is different and include
85/// in the error description.
86///
87/// This is very expensive for values that differ
88fn attr_equal<T, F>(attr: &str, a: &Artifacts, b: &Artifacts, get_attr: &F) -> Result<()>
89where
90    T: Debug + PartialEq,
91    F: Fn(&Artifact) -> &T,
92{
93    let mut diff: Vec<String> = Vec::new();
94
95    for (a_name, a_art) in a.iter() {
96        let b_art = b.get(a_name).unwrap();
97        let a_attr = get_attr(a_art);
98        let b_attr = get_attr(b_art);
99        if a_attr != b_attr {
100            let mut a_str = format!("{:?}", a_attr);
101            let mut b_str = format!("{:?}", b_attr);
102            let a_big = if a_str.len() > 100 { "..." } else { "" };
103            let b_big = if b_str.len() > 100 { "..." } else { "" };
104            a_str.truncate(100);
105            b_str.truncate(100);
106            diff.push(format!(
107                "[{}: {}{} != {}{}]",
108                a_name,
109                a_str,
110                a_big,
111                b_str,
112                b_big
113            ));
114        }
115    }
116
117    if diff.is_empty() {
118        Ok(())
119    } else {
120        Err(
121            ErrorKind::NotEqual(format!("{} different: {:?}", attr, diff)).into(),
122        )
123    }
124}
125
126/// num *approximately* equal
127fn float_equal<F>(attr: &str, a: &Artifacts, b: &Artifacts, get_num: &F) -> Result<()>
128where
129    F: Fn(&Artifact) -> f32,
130{
131    let mut diff: Vec<String> = Vec::new();
132    fn thous(f: f32) -> i64 {
133        (f * 1000.) as i64
134    }
135
136    for (a_name, a_art) in a.iter() {
137        let b_art = b.get(a_name).unwrap();
138        let a_attr = get_num(a_art);
139        let b_attr = get_num(b_art);
140        if thous(a_attr) != thous(b_attr) {
141            let mut a_str = format!("{:?}", a_attr);
142            let mut b_str = format!("{:?}", b_attr);
143            a_str.truncate(50);
144            b_str.truncate(50);
145            diff.push(format!("({}, {} != {})", a_name, a_str, b_str));
146        }
147    }
148
149    if diff.is_empty() {
150        Ok(())
151    } else {
152        Err(
153            ErrorKind::NotEqual(format!("{} different: {:?}", attr, diff)).into(),
154        )
155    }
156}
157
158fn proj_attr_equal<T>(attr: &str, a: &T, b: &T) -> Result<()>
159where
160    T: Debug + PartialEq,
161{
162    if a != b {
163        Err(
164            ErrorKind::NotEqual(format!("{} FIRST:\n{:?}\n\nSECOND:\n{:?}", attr, a, b)).into(),
165        )
166    } else {
167        Ok(())
168    }
169}