Skip to main content

sysml_core/
scaffold.rs

1use serde::Serialize;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub enum ScaffoldKind {
5    Requirement,
6    Verification,
7    Part,
8    Package,
9    UseCase,
10    Action,
11    State,
12    Interface,
13}
14
15impl std::str::FromStr for ScaffoldKind {
16    type Err = String;
17
18    fn from_str(s: &str) -> Result<Self, Self::Err> {
19        match s.to_lowercase().as_str() {
20            "requirement" | "req" => Ok(Self::Requirement),
21            "verification" | "verify" => Ok(Self::Verification),
22            "part" => Ok(Self::Part),
23            "package" | "pkg" => Ok(Self::Package),
24            "use-case" | "usecase" | "use_case" => Ok(Self::UseCase),
25            "action" => Ok(Self::Action),
26            "state" => Ok(Self::State),
27            "interface" => Ok(Self::Interface),
28            _ => Err(format!(
29                "Unknown scaffold kind: '{}'. Valid: requirement, verification, part, package, use-case, action, state, interface",
30                s
31            )),
32        }
33    }
34}
35
36#[derive(Debug, Clone, Serialize)]
37pub struct ScaffoldResult {
38    pub kind: String,
39    pub name: String,
40    pub sysml: String,
41}
42
43pub fn generate(kind: ScaffoldKind, name: &str) -> ScaffoldResult {
44    let sysml = match kind {
45        ScaffoldKind::Requirement => format!(
46            r#"requirement def {name} {{
47    doc /* TODO: describe requirement */
48
49    attribute id : String;
50    attribute text : String;
51}}"#
52        ),
53        ScaffoldKind::Verification => format!(
54            r#"verification def {name} {{
55    doc /* TODO: describe verification */
56
57    subject testSubject;
58
59    objective {{
60        doc /* TODO: verification objective */
61    }}
62}}"#
63        ),
64        ScaffoldKind::Part => format!(
65            r#"part def {name} {{
66    doc /* TODO: describe part */
67
68    attribute mass : Real;
69
70    port controlPort : ControlPort;
71}}"#
72        ),
73        ScaffoldKind::Package => format!(
74            r#"package {name} {{
75    doc /* TODO: package description */
76
77}}"#
78        ),
79        ScaffoldKind::UseCase => format!(
80            r#"use case def {name} {{
81    doc /* TODO: describe use case */
82
83    subject systemUnderTest;
84
85    objective {{
86        doc /* TODO: use case objective */
87    }}
88}}"#
89        ),
90        ScaffoldKind::Action => format!(
91            r#"action def {name} {{
92    doc /* TODO: describe action */
93
94    in item input;
95    out item output;
96}}"#
97        ),
98        ScaffoldKind::State => format!(
99            r#"state def {name} {{
100    doc /* TODO: describe state machine */
101
102    entry action entryAction;
103    exit action exitAction;
104}}"#
105        ),
106        ScaffoldKind::Interface => format!(
107            r#"interface def {name} {{
108    doc /* TODO: describe interface */
109
110    end port supplier;
111    end port consumer;
112}}"#
113        ),
114    };
115
116    ScaffoldResult {
117        kind: format!("{kind:?}"),
118        name: name.to_string(),
119        sysml,
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    use super::*;
126
127    #[test]
128    fn test_scaffold_requirement() {
129        let result = generate(ScaffoldKind::Requirement, "MFRQ99");
130        assert!(result.sysml.contains("requirement def MFRQ99"));
131        assert!(result.sysml.contains("attribute id"));
132    }
133
134    #[test]
135    fn test_scaffold_part() {
136        let result = generate(ScaffoldKind::Part, "NewModule");
137        assert!(result.sysml.contains("part def NewModule"));
138        assert!(result.sysml.contains("port controlPort"));
139    }
140
141    #[test]
142    fn test_scaffold_package() {
143        let result = generate(ScaffoldKind::Package, "NewPkg");
144        assert!(result.sysml.contains("package NewPkg"));
145    }
146
147    #[test]
148    fn test_scaffold_verification() {
149        let result = generate(ScaffoldKind::Verification, "VerifyShield");
150        assert!(result.sysml.contains("verification def VerifyShield"));
151        assert!(result.sysml.contains("subject testSubject"));
152    }
153
154    #[test]
155    fn test_scaffold_use_case() {
156        let result = generate(ScaffoldKind::UseCase, "MiningOp");
157        assert!(result.sysml.contains("use case def MiningOp"));
158    }
159
160    #[test]
161    fn test_scaffold_action() {
162        let result = generate(ScaffoldKind::Action, "DetectThreat");
163        assert!(result.sysml.contains("action def DetectThreat"));
164    }
165
166    #[test]
167    fn test_scaffold_state() {
168        let result = generate(ScaffoldKind::State, "ShipStates");
169        assert!(result.sysml.contains("state def ShipStates"));
170    }
171
172    #[test]
173    fn test_scaffold_interface() {
174        let result = generate(ScaffoldKind::Interface, "PowerInterface");
175        assert!(result.sysml.contains("interface def PowerInterface"));
176    }
177
178    #[test]
179    fn test_parse_kind() {
180        assert_eq!(
181            "requirement".parse::<ScaffoldKind>().unwrap(),
182            ScaffoldKind::Requirement
183        );
184        assert_eq!(
185            "req".parse::<ScaffoldKind>().unwrap(),
186            ScaffoldKind::Requirement
187        );
188        assert_eq!(
189            "use-case".parse::<ScaffoldKind>().unwrap(),
190            ScaffoldKind::UseCase
191        );
192        assert!("invalid".parse::<ScaffoldKind>().is_err());
193    }
194
195    #[test]
196    fn test_scaffold_parseable() {
197        use nomograph_core::traits::Parser;
198        let parser = crate::parser::SysmlParser::new();
199        for kind in [
200            ScaffoldKind::Requirement,
201            ScaffoldKind::Part,
202            ScaffoldKind::Package,
203            ScaffoldKind::UseCase,
204            ScaffoldKind::Action,
205            ScaffoldKind::State,
206            ScaffoldKind::Interface,
207        ] {
208            let result = generate(kind, "TestName");
209            let parse_result = parser.parse(&result.sysml, std::path::Path::new("scaffold.sysml"));
210            assert!(
211                parse_result.is_ok(),
212                "Scaffold {:?} produced unparseable SysML: {}",
213                kind,
214                result.sysml
215            );
216        }
217    }
218}