artifact_app/
export.rs

1//! Methods for exporting artifact to other data types (like json)
2
3use serde_json;
4use uuid::Uuid;
5
6use dev_prefix::*;
7use types::*;
8use cmd::check;
9use utils::UUID;
10
11#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
12pub struct LocData {
13    pub path: String,
14    pub line: u64,
15}
16
17#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
18pub struct ArtifactData {
19    pub id: u64,
20    pub revision: u64,
21    pub name: String,
22    pub def: String,
23    pub text: String,
24    pub partof: Vec<String>,
25
26    // // TODO: until I serde gets up to speed, the web-api will
27    // // have to send these values even though they are ignored
28    #[serde(default)] pub parts: Vec<String>,
29    #[serde(default)] pub code: Option<LocData>,
30    #[serde(default)] pub done: Option<String>,
31    #[serde(default = "default_comp_tested")] pub completed: f32,
32    #[serde(default = "default_comp_tested")] pub tested: f32,
33}
34
35#[derive(Serialize, Debug, Default, Clone, PartialEq)]
36pub struct ProjectData {
37    pub artifacts: Vec<ArtifactData>,
38    pub files: Vec<String>,
39    pub checked: String,
40    pub uuid: Uuid,
41}
42
43fn default_comp_tested() -> f32 {
44    -1.0_f32
45}
46
47impl Project {
48    pub fn to_data(&self) -> ProjectData {
49        let artifacts = self.artifacts
50            .iter()
51            .map(|(n, a)| a.to_data(&self.origin, n))
52            .collect();
53
54        let files: Vec<String> = self.files
55            .iter()
56            .map(|p| {
57                p.strip_prefix(&self.origin)
58                    .expect("origin invalid")
59                    .to_string_lossy()
60                    .to_string()
61            })
62            .collect();
63
64        let mut checked: Vec<u8> = Vec::new();
65        let cmd = check::Cmd { color: false };
66
67        check::display_check(&mut checked, &self.origin, self, &cmd);
68
69        ProjectData {
70            artifacts: artifacts,
71            files: files,
72            checked: String::from_utf8(checked).expect("invalid-utf8 from checked"),
73            uuid: *UUID,
74        }
75    }
76}
77
78impl Artifact {
79    /// convert an `Artifact` to it's data form
80    pub fn to_data(&self, origin: &Path, name: &NameRc) -> ArtifactData {
81        let (code, done) = match self.done {
82            Done::Code(ref l) => (
83                Some(LocData {
84                    path: l.path
85                        .strip_prefix(origin)
86                        .expect("origin invalid")
87                        .to_string_lossy()
88                        .to_string(),
89                    line: l.line as u64,
90                }),
91                None,
92            ),
93            Done::Defined(ref s) => (None, Some(s.clone())),
94            Done::NotDone => (None, None),
95        };
96        let mut partof: Vec<_> = self.partof.iter().map(|n| n.raw.clone()).collect();
97        let mut parts: Vec<_> = self.parts.iter().map(|n| n.raw.clone()).collect();
98
99        partof.sort();
100        parts.sort();
101        let path = self.def
102            .strip_prefix(origin)
103            .expect("origin invalid")
104            .to_string_lossy()
105            .to_string();
106
107        ArtifactData {
108            id: self.id,
109            revision: self.revision,
110            name: name.raw.clone(),
111            def: path,
112            text: self.text.clone(),
113            partof: partof,
114            parts: parts,
115            code: code,
116            done: done,
117            completed: self.completed,
118            tested: self.tested,
119        }
120    }
121
122    /// Get an `Artifact` from it's data form
123    pub fn from_data(repo: &Path, data: &ArtifactData) -> Result<(NameRc, Artifact)> {
124        let name = try!(NameRc::from_str(&data.name));
125        let mut partof: HashSet<NameRc> = HashSet::new();
126        for p in &data.partof {
127            let pname = try!(NameRc::from_str(p));
128            partof.insert(pname);
129        }
130        let done = if data.done.is_some() && data.code.is_some() {
131            let msg = "has both done and code defined".to_string();
132            return Err(ErrorKind::InvalidArtifact(data.name.clone(), msg).into());
133        } else if let Some(ref d) = data.done {
134            if d == "" {
135                return Err(
136                    ErrorKind::InvalidAttr(
137                        name.to_string(),
138                        "done cannot be an empty string.".to_string(),
139                    ).into(),
140                );
141            }
142            Done::Defined(d.clone())
143        } else if let Some(ref c) = data.code {
144            Done::Code(Loc {
145                path: repo.join(&c.path),
146                line: c.line as usize,
147            })
148        } else {
149            Done::NotDone
150        };
151
152        Ok((
153            name,
154            Artifact {
155                id: data.id,
156                revision: data.revision,
157                def: repo.join(&data.def),
158                text: data.text.clone(),
159                partof: partof,
160                done: done,
161                parts: HashSet::new(),
162                completed: -1.0,
163                tested: -1.0,
164            },
165        ))
166    }
167}
168
169/// convert the project's artifacts to a json list
170pub fn project_artifacts_to_json(project: &Project, names: Option<&[NameRc]>) -> String {
171    let out_arts: Vec<_> = if let Some(names) = names {
172        names
173            .iter()
174            .map(|n| project.artifacts[n].to_data(&project.origin, n))
175            .collect()
176    } else {
177        project
178            .artifacts
179            .iter()
180            .map(|(n, a)| a.to_data(&project.origin, n))
181            .collect()
182    };
183
184    let value = serde_json::to_value(out_arts).unwrap();
185    serde_json::to_string(&value).unwrap()
186}
187
188#[test]
189fn test_serde() {
190    let artifact = ArtifactData {
191        id: 10,
192        revision: 0,
193        name: "name".to_string(),
194        def: "path".to_string(),
195        text: "text".to_string(),
196        partof: Vec::from_iter(vec!["partof-1".to_string()]),
197        parts: Vec::from_iter(vec!["part-1".to_string()]),
198        done: None,
199        code: Some(LocData {
200            path: "path".to_string(),
201            line: 10,
202        }),
203        completed: 0.,
204        tested: 0.,
205    };
206
207    let serialized = serde_json::to_string(&artifact).unwrap();
208    let deserialized: ArtifactData = serde_json::from_str(&serialized).unwrap();
209
210    assert_eq!(artifact, deserialized);
211
212
213    // TODO: enable this test
214    // load an artifact with defaults
215    //    let with_defaults = r#"
216    // {
217    //    "id": 10,
218    //    "name": "name",
219    //    "path": "path",
220    //    "text": "text",
221    //    "partof": ["partof-1"],
222    // }"#;
223    //    let deserialized: ArtifactData = serde_json::from_str(with_defaults).unwrap();
224    //    artifact.parts = vec![];
225    //    artifact.loc = None;
226    //    artifact.completed = -1;
227    //    artifact.tested = -1;
228    //    assert_eq!(artifact, deserialized);
229}