1use std::path::Path;
2
3use nomograph_core::traits::Parser;
4use nomograph_core::types::{Diagnostic, ParseResult};
5
6use crate::element::SysmlElement;
7use crate::relationship::SysmlRelationship;
8use crate::walker::{collect_parse_errors, Walker};
9
10pub struct SysmlParser;
11
12impl SysmlParser {
13 pub fn new() -> Self {
14 Self
15 }
16}
17
18impl Default for SysmlParser {
19 fn default() -> Self {
20 Self::new()
21 }
22}
23
24impl Parser for SysmlParser {
25 type Elem = SysmlElement;
26 type Rel = SysmlRelationship;
27 type Error = nomograph_core::CoreError;
28
29 fn parse(
30 &self,
31 source: &str,
32 path: &Path,
33 ) -> Result<ParseResult<Self::Elem, Self::Rel>, Self::Error> {
34 let mut ts_parser = tree_sitter::Parser::new();
35 ts_parser
36 .set_language(&tree_sitter_sysml::LANGUAGE.into())
37 .map_err(|e| nomograph_core::CoreError::Parse(e.to_string()))?;
38
39 let tree = ts_parser
40 .parse(source, None)
41 .ok_or_else(|| nomograph_core::CoreError::Parse("Parser returned None".to_string()))?;
42
43 let root = tree.root_node();
44
45 let mut diagnostics = Vec::new();
46 collect_parse_errors(root, source, &mut diagnostics);
47
48 let mut walker = Walker::new(source, path.to_path_buf());
49 walker.walk_root(root);
50
51 Ok(ParseResult {
52 elements: walker.elements,
53 relationships: walker.relationships,
54 diagnostics,
55 })
56 }
57
58 fn validate(&self, source: &str) -> Vec<Diagnostic> {
59 let mut ts_parser = tree_sitter::Parser::new();
60 if ts_parser
61 .set_language(&tree_sitter_sysml::LANGUAGE.into())
62 .is_err()
63 {
64 return vec![Diagnostic {
65 severity: nomograph_core::types::Severity::Error,
66 message: "Failed to initialize parser".to_string(),
67 span: nomograph_core::types::Span {
68 start_line: 0,
69 start_col: 0,
70 end_line: 0,
71 end_col: 0,
72 },
73 }];
74 }
75
76 let Some(tree) = ts_parser.parse(source, None) else {
77 return vec![Diagnostic {
78 severity: nomograph_core::types::Severity::Error,
79 message: "Parser returned None".to_string(),
80 span: nomograph_core::types::Span {
81 start_line: 0,
82 start_col: 0,
83 end_line: 0,
84 end_col: 0,
85 },
86 }];
87 };
88
89 let mut diagnostics = Vec::new();
90 collect_parse_errors(tree.root_node(), source, &mut diagnostics);
91 diagnostics
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98 use nomograph_core::types::Severity;
99 use std::path::PathBuf;
100
101 fn test_path() -> PathBuf {
102 PathBuf::from("test.sysml")
103 }
104
105 fn eve_fixture(name: &str) -> String {
106 let base = env!("CARGO_MANIFEST_DIR");
107 let path = format!("{}/../../tests/fixtures/eve/DomainModel/{}", base, name);
108 std::fs::read_to_string(&path)
109 .unwrap_or_else(|e| panic!("Failed to read fixture {}: {}", path, e))
110 }
111
112 #[test]
113 fn test_parse_simple_package() {
114 let parser = SysmlParser::new();
115 let source = "package Test { part def Engine; }";
116 let result = parser.parse(source, &test_path()).unwrap();
117
118 assert!(!result.elements.is_empty());
119 let names: Vec<&str> = result
120 .elements
121 .iter()
122 .map(|e| e.qualified_name.as_str())
123 .collect();
124 assert!(names.contains(&"Test"), "missing Test package");
125 assert!(names.contains(&"Test::Engine"), "missing Test::Engine");
126 }
127
128 #[test]
129 fn test_parse_element_kinds() {
130 let parser = SysmlParser::new();
131 let source = "package Test { part def Engine; }";
132 let result = parser.parse(source, &test_path()).unwrap();
133
134 let engine = result
135 .elements
136 .iter()
137 .find(|e| e.qualified_name == "Test::Engine")
138 .unwrap();
139 assert_eq!(engine.kind, "part_definition");
140 }
141
142 #[test]
143 fn test_parse_produces_relationships() {
144 let parser = SysmlParser::new();
145 let source = "package Test { part def Engine; part engine : Engine; }";
146 let result = parser.parse(source, &test_path()).unwrap();
147
148 assert!(!result.relationships.is_empty());
149 let has_typed_by = result.relationships.iter().any(|r| r.kind == "TypedBy");
150 assert!(has_typed_by, "should have TypedBy relationship");
151 }
152
153 #[test]
154 fn test_validate_valid_sysml() {
155 let parser = SysmlParser::new();
156 let source = "package Test { part def Engine; }";
157 let diagnostics = parser.validate(source);
158
159 let errors: Vec<_> = diagnostics
160 .iter()
161 .filter(|d| d.severity == Severity::Error)
162 .collect();
163 assert!(
164 errors.is_empty(),
165 "valid SysML should have no errors, got: {:?}",
166 errors
167 );
168 }
169
170 #[test]
171 fn test_validate_invalid_sysml() {
172 let parser = SysmlParser::new();
173 let source = "this is not sysml {{{";
174 let diagnostics = parser.validate(source);
175
176 let has_errors = diagnostics.iter().any(|d| d.severity == Severity::Error);
177 assert!(has_errors, "invalid SysML should have errors");
178 }
179
180 #[test]
181 fn test_parse_eve_mining_frigate() {
182 let parser = SysmlParser::new();
183 let source = eve_fixture("MiningFrigate.sysml");
184 let path = PathBuf::from("MiningFrigate.sysml");
185 let result = parser.parse(&source, &path).unwrap();
186
187 assert!(
188 !result.elements.is_empty(),
189 "should extract elements from MiningFrigate.sysml"
190 );
191 assert!(
192 !result.relationships.is_empty(),
193 "should extract relationships"
194 );
195 }
196
197 #[test]
198 fn test_parse_eve_requirements() {
199 let parser = SysmlParser::new();
200 let source = eve_fixture("MiningFrigateRequirements.sysml");
201 let path = PathBuf::from("MiningFrigateRequirements.sysml");
202 let result = parser.parse(&source, &path).unwrap();
203
204 assert!(!result.elements.is_empty());
205 let req_elements: Vec<_> = result
206 .elements
207 .iter()
208 .filter(|e| e.kind.contains("requirement"))
209 .collect();
210 assert!(!req_elements.is_empty(), "should have requirement elements");
211 }
212
213 #[test]
214 fn test_parse_file_path_stored() {
215 let parser = SysmlParser::new();
216 let source = "package Test { part def Engine; }";
217 let path = PathBuf::from("my/test.sysml");
218 let result = parser.parse(source, &path).unwrap();
219
220 for elem in &result.elements {
221 assert_eq!(elem.file_path, path);
222 }
223 }
224}