artifact_app/
types.rs

1#![allow(unused_doc_comment)]
2
3use dev_prefix::*;
4
5pub const VERSION: &'static str = env!("CARGO_PKG_VERSION");
6/// variable which can be used in settings path to mean the repo directory
7pub const REPO_VAR: &'static str = "repo";
8/// variable which can be used in settings paths to mean the dir of the settings file.
9/// #TODO: remove this
10pub const CWD_VAR: &'static str = "cwd";
11/// base definition of a valid name. Some pieces may ignore case.
12pub const NAME_VALID_STR: &'static str = "(?:REQ|SPC|TST)(?:-[A-Z0-9_-]*[A-Z0-9_])?";
13
14lazy_static!{
15    // must start with artifact type, followed by "-", followed by at least 1 valid character
16    // cannot end with "-"
17    pub static ref NAME_VALID: Regex = Regex::new(
18        &format!("^{}$", NAME_VALID_STR)).unwrap();
19    pub static ref REPO_DIR: PathBuf = PathBuf::from(".art");
20    pub static ref SETTINGS_PATH: PathBuf = REPO_DIR.join("settings.toml");
21}
22
23error_chain! {
24    types {
25        Error, ErrorKind, ResultExt, Result;
26    }
27
28    links {
29        // no external error chains (yet)
30    }
31
32    foreign_links {
33        // stdlib
34        Io(::std::io::Error);
35        Fmt(::std::fmt::Error);
36
37        // crates
38        StrFmt(::strfmt::FmtError);
39        TomlError(::toml::de::Error);
40    }
41
42    errors {
43        // Loading errors
44        Load(desc: String) {
45            description("Misc error while loading artifacts")
46            display("Error loading: {}", desc)
47        }
48        TomlParse(locs: String) {
49            description("Error while parsing TOML file")
50            display("Error parsing TOML: {}", locs)
51        }
52        MissingTable {
53            description("Must contain a single table")
54        }
55        InvalidName(desc: String) {
56            description("Invalid artifact name")
57            display("Invalid artifact name: \"{}\"", desc)
58        }
59        InvalidAttr(name: String, attr: String) {
60            description("Artifact has invalid attribute")
61            display("Artifact {} has invalid attribute: {}", name, attr)
62        }
63        InvalidSettings(desc: String) {
64            description("Invalid settings")
65            display("Invalid settings: {}", desc)
66        }
67        InvalidArtifact(name: String, desc: String) {
68            description("Invalid artifact")
69            display("Artifact {} is invalid: {}", name, desc)
70        }
71        MissingParent(name: String, parent: String) {
72            description("Missing parent artifact")
73            display("Parent {} does not exist for {}", parent, name)
74        }
75        // Processing errors
76        InvalidTextVariables {
77            description("Couldn't resolve some text variables")
78        }
79        InvalidPartof {
80            description("Some artifacts have invalid partof attributes")
81        }
82        InvalidDone {
83            description("Some artifacts have invalid partof attributes")
84        }
85        NameNotFound(desc: String) {
86            description("Searched for names were not found")
87            display("The following artifacts do not exists: {}", desc)
88        }
89        LocNotFound {
90            description("Errors while finding implementation locations")
91        }
92        DoneTwice(desc: String) {
93            description("The artifact is done and implemented in code")
94            display("Referenced in code and `done` is set: {}", desc)
95        }
96        InvalidUnicode(path: String) {
97            description("We do not yet support non-unicode paths")
98            display("Invalid unicode in path: {}", path)
99        }
100
101        // Cmd errors
102        CmdError(desc: String) {
103            description("Error while running a command")
104            display("{}", desc)
105        }
106
107        // Misc errors
108        PathNotFound(desc: String) {
109            description("Invalid path")
110            display("Path does not exist: {}", desc)
111        }
112        NotEqual(desc: String) {
113            description("Values not equal")
114            display("{}", desc)
115        }
116        Security(desc: String) {
117            description("Security vulnerability detected")
118            display("Security vulnerability: {}", desc)
119        }
120        Internal(desc: String) {
121            description("Internal error")
122            display("Internal error: {}", desc)
123        }
124        NothingDone {
125            description("Internal control flow")
126        }
127    }
128}
129
130/// our `from_str` can throw errors
131pub trait LoadFromStr: Sized {
132    fn from_str(s: &str) -> Result<Self>;
133}
134
135/// Artifacts organized by name
136pub type Artifacts = HashMap<NameRc, Artifact>;
137/// Names in a `HashSet` for fast lookup
138pub type Names = HashSet<NameRc>;
139pub type NameRc = Arc<Name>;
140
141/// represents the results and all the data necessary
142/// to reconstruct a loaded project
143#[derive(Debug, Clone)]
144pub struct Project {
145    pub artifacts: Artifacts,
146    pub settings: Settings,
147    pub files: HashSet<PathBuf>,
148    pub dne_locs: HashMap<Name, Loc>,
149
150    // preserved locations where each piece is from
151    pub origin: PathBuf,
152    pub repo_map: HashMap<PathBuf, PathBuf>,
153}
154
155impl Default for Project {
156    fn default() -> Project {
157        Project {
158            artifacts: Artifacts::default(),
159            settings: Settings::default(),
160            files: HashSet::default(),
161            dne_locs: HashMap::default(),
162            origin: PathBuf::default(),
163            repo_map: HashMap::default(),
164        }
165    }
166}
167
168/// Definition of an artifact name, with Traits for hashing,
169/// displaying, etc
170// note: methods are implemented in name.rs
171#[derive(Clone)]
172pub struct Name {
173    /// user definition
174    pub raw: String,
175    /// standardized version
176    pub value: Vec<String>,
177    /// the inferred type of the artifact
178    pub ty: Type,
179}
180
181#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
182/// type of an `Artifact`
183pub enum Type {
184    REQ,
185    SPC,
186    TST,
187}
188
189/// location in a file
190#[derive(Debug, Clone, PartialEq)]
191pub struct Loc {
192    pub path: PathBuf,
193    pub line: usize,
194}
195
196#[cfg(test)]
197impl Loc {
198    pub fn fake() -> Loc {
199        Loc {
200            path: Path::new("fake").to_path_buf(),
201            line: 42,
202        }
203    }
204}
205
206impl fmt::Display for Loc {
207    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
208        write!(f, "{}[{}]", self.path.display(), self.line)
209    }
210}
211
212/// Determines if the artifact is "done by definition"
213///
214/// It is done by definition if:
215/// - it is found in source code
216/// - it has it's `done` field set
217#[derive(Debug, Clone, PartialEq)]
218pub enum Done {
219    /// Artifact is implemented in code
220    Code(Loc),
221    /// artifact has it's `done` field defined
222    Defined(String),
223    /// artifact is NOT "done by definition"
224    NotDone,
225}
226
227impl Done {
228    /// return true if Done == Code || Defined
229    pub fn is_done(&self) -> bool {
230        match *self {
231            Done::Code(_) | Done::Defined(_) => true,
232            Done::NotDone => false,
233        }
234    }
235}
236
237impl fmt::Display for Done {
238    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
239        match *self {
240            Done::Code(ref c) => write!(f, "{}", c),
241            Done::Defined(ref s) => write!(f, "{}", s),
242            Done::NotDone => write!(f, "not done"),
243        }
244    }
245}
246
247/// The Artifact type. This encapsulates
248/// REQ, SPC, and TST artifacts and
249/// contains space to link them
250/// #SPC-artifact
251#[derive(Clone, Debug, PartialEq)]
252pub struct Artifact {
253    /// constant id for this instance
254    pub id: u64,
255    /// revision id for edit functionality
256    pub revision: u64,
257    /// path of definition (.toml file)
258    pub def: PathBuf,
259    /// `text` attr
260    pub text: String,
261    /// explicit and calculated `partof` attribute
262    pub partof: Names,
263    /// parts is inverse of partof (calculated)
264    pub parts: Names,
265    /// `done` attribute, allows user to "define as done"
266    pub done: Done,
267    /// completed ratio (calculated)
268    pub completed: f32,
269    /// tested ratio (calculated)
270    pub tested: f32,
271}
272
273/// repo settings for loading artifacts
274/// #SPC-project-settings
275#[derive(Debug, Default, Clone, PartialEq)]
276pub struct Settings {
277    pub artifact_paths: HashSet<PathBuf>,
278    pub exclude_artifact_paths: HashSet<PathBuf>,
279    pub code_paths: HashSet<PathBuf>,
280    pub exclude_code_paths: HashSet<PathBuf>,
281}
282
283impl Settings {
284    pub fn new() -> Settings {
285        Settings {
286            artifact_paths: HashSet::new(),
287            exclude_artifact_paths: HashSet::new(),
288            code_paths: HashSet::new(),
289            exclude_code_paths: HashSet::new(),
290        }
291    }
292}
293
294#[derive(Debug, Default, Clone, Serialize)]
295/// struct that is passed to the api server
296pub struct ServeCmd {
297    pub addr: String,
298    pub readonly: bool,
299    pub path_url: String,
300}