Skip to main content

teamcraft/domain/
references.rs

1//! BOM revision rules, references and change propagation.
2
3use serde::{Deserialize, Serialize};
4
5/// Decides which revision of a child item is shown in a BOM.
6#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
7#[serde(rename_all = "snake_case")]
8pub enum RevisionRule {
9    /// The newest revision regardless of its state.
10    #[default]
11    Latest,
12    /// The newest released revision; lines without one are flagged.
13    Released,
14    /// Released assemblies show exactly the child revisions they were
15    /// released with; lines of unreleased assemblies use the newest
16    /// released revision.
17    AsReleased,
18}
19
20impl RevisionRule {
21    pub fn as_str(self) -> &'static str {
22        match self {
23            RevisionRule::Latest => "latest",
24            RevisionRule::Released => "released",
25            RevisionRule::AsReleased => "as_released",
26        }
27    }
28
29    pub fn parse(s: &str) -> Option<RevisionRule> {
30        match s.trim().to_ascii_lowercase().as_str() {
31            "latest" | "" => Some(RevisionRule::Latest),
32            "released" => Some(RevisionRule::Released),
33            "as_released" | "as-released" => Some(RevisionRule::AsReleased),
34            _ => None,
35        }
36    }
37}
38
39// ---------------------------------------------------------------------------
40// References and change propagation
41// ---------------------------------------------------------------------------
42
43/// How a revision references another item besides the BOM.
44#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
45#[serde(rename_all = "snake_case")]
46pub enum ReferenceKind {
47    /// The referencing item is a drawing of the target.
48    Drawing,
49    /// The referencing item is a specification that applies to the target.
50    Specification,
51    /// The referencing model uses geometry of the target (skeleton,
52    /// interface, mating part).
53    Reference,
54}
55
56impl ReferenceKind {
57    pub const ALL: [ReferenceKind; 3] = [
58        ReferenceKind::Drawing,
59        ReferenceKind::Specification,
60        ReferenceKind::Reference,
61    ];
62
63    pub fn as_str(self) -> &'static str {
64        match self {
65            ReferenceKind::Drawing => "drawing",
66            ReferenceKind::Specification => "specification",
67            ReferenceKind::Reference => "reference",
68        }
69    }
70
71    /// "`<this item>` … `<target>`", e.g. "is a drawing of".
72    pub fn label(self) -> &'static str {
73        match self {
74            ReferenceKind::Drawing => "Drawing of",
75            ReferenceKind::Specification => "Specification for",
76            ReferenceKind::Reference => "References",
77        }
78    }
79
80    pub fn parse(s: &str) -> Option<ReferenceKind> {
81        match s.trim().to_ascii_lowercase().as_str() {
82            "drawing" => Some(ReferenceKind::Drawing),
83            "specification" => Some(ReferenceKind::Specification),
84            "reference" => Some(ReferenceKind::Reference),
85            _ => None,
86        }
87    }
88}
89
90/// Found by cargo mutants: the stored values (URLs, API, database) and the
91/// labels were not tested.
92#[cfg(test)]
93mod tests {
94    use super::*;
95
96    #[test]
97    fn revision_rules() {
98        let rules = [
99            RevisionRule::Latest,
100            RevisionRule::Released,
101            RevisionRule::AsReleased,
102        ];
103        let stored: Vec<&str> = rules.iter().map(|r| r.as_str()).collect();
104        assert_eq!(stored, ["latest", "released", "as_released"]);
105        for r in rules {
106            assert_eq!(RevisionRule::parse(r.as_str()), Some(r));
107        }
108        assert_eq!(RevisionRule::parse(""), Some(RevisionRule::Latest));
109        assert_eq!(
110            RevisionRule::parse("as-released"),
111            Some(RevisionRule::AsReleased)
112        );
113        assert_eq!(RevisionRule::parse("newest"), None);
114    }
115
116    #[test]
117    fn reference_kinds() {
118        let kinds: Vec<(&str, &str)> = ReferenceKind::ALL
119            .iter()
120            .map(|k| (k.as_str(), k.label()))
121            .collect();
122        assert_eq!(
123            kinds,
124            [
125                ("drawing", "Drawing of"),
126                ("specification", "Specification for"),
127                ("reference", "References")
128            ]
129        );
130        for k in ReferenceKind::ALL {
131            assert_eq!(ReferenceKind::parse(k.as_str()), Some(k));
132        }
133        assert_eq!(ReferenceKind::parse("link"), None);
134    }
135}