1#[allow(dead_code)]
2use crate::ast::{MonDocument, MonValue, SymbolTable, TypeSpec};
3use crate::error::MonError;
4
5#[cfg(feature = "lsp")]
6use crate::ast::{Member, MonValueKind};
7#[cfg(feature = "lsp")]
8use crate::lsp;
9use crate::parser::Parser;
10use crate::resolver::Resolver;
11use crate::serialization::{to_value, Value};
12#[cfg(feature = "lsp")]
13use miette::SourceSpan;
14use serde::{Serialize, Serializer};
15use serde_json;
16use serde_yaml;
17use std::collections::HashMap;
18use std::fmt::Display;
19use std::path::PathBuf;
20
21pub struct AnalysisResult {
26 pub document: MonDocument,
27 pub unresolved_document: MonDocument,
28 pub symbol_table: SymbolTable,
29 pub anchors: HashMap<String, MonValue>,
30}
31
32impl Serialize for AnalysisResult {
33 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
34 where
35 S: Serializer,
36 {
37 let value = self.to_value();
38 value.serialize(serializer)
39 }
40}
41
42impl AnalysisResult {
43 #[must_use]
45 pub fn to_value(&self) -> Value {
46 to_value(&self.document.root)
47 }
48
49 pub fn to_json(&self) -> Result<String, serde_json::Error> {
54 serde_json::to_string_pretty(&self)
55 }
56
57 pub fn to_yaml(&self) -> Result<String, serde_yaml::Error> {
62 serde_yaml::to_string(&self)
63 }
64
65 #[cfg(feature = "lsp")]
66 #[must_use]
69 pub fn get_definition_at(&self, position: usize) -> Option<SourceSpan> {
70 let node = find_node_at(&self.unresolved_document.root, position)?;
71
72 match node {
73 FoundNode::Value(value) => match &value.kind {
74 MonValueKind::Alias(alias_name) => {
75 let anchor_def = self.anchors.get(alias_name)?;
76 Some(anchor_def.get_source_span())
77 }
78 _ => None,
79 },
80 FoundNode::TypeSpec(type_spec) => match type_spec {
81 TypeSpec::Simple(name, _) => {
82 let type_def = self.symbol_table.types.get(name)?;
83 Some(type_def.def_type.get_span())
84 }
85 _ => None,
86 },
87 }
88 }
89
90 #[cfg(feature = "lsp")]
91 #[must_use]
94 pub fn get_type_info_at(&self, position: usize) -> Option<String> {
95 let symbol_info = lsp::find_symbol_at(&self.unresolved_document.root, position)?;
96
97 if let Some(validation) = symbol_info.validation {
98 return Some(validation.to_string());
99 }
100
101 match symbol_info.node {
102 lsp::FoundNode::Value(value) => Some(value.kind.to_string()),
103 lsp::FoundNode::TypeSpec(type_spec) => Some(type_spec.to_string()),
104 }
105 }
106 #[cfg(feature = "lsp")]
107 pub fn find_references(&self, position: usize) -> Option<Vec<SourceSpan>> {
109 let symbol_info = lsp::find_symbol_at(&self.unresolved_document.root, position)?;
110
111 let (name_to_find, definition_span) = match symbol_info.node {
112 lsp::FoundNode::Value(value) => match &value.kind {
113 MonValueKind::Alias(alias_name) => {
114 let anchor_def = self.anchors.get(alias_name)?;
115 (alias_name.clone(), anchor_def.get_source_span())
116 }
117 _ => return None,
118 },
119 lsp::FoundNode::TypeSpec(type_spec) => match type_spec {
120 TypeSpec::Simple(name, _) => {
121 let type_def = self.symbol_table.types.get(name)?;
122 (name.clone(), type_def.name_span)
123 }
124 _ => return None,
125 },
126 };
127
128 let usages = lsp::find_all_usages(&self.unresolved_document.root, &name_to_find)
129 .into_iter()
130 .filter(|span| *span != definition_span)
131 .collect();
132 Some(usages)
133 }
134}
135
136#[cfg(feature = "lsp")]
137#[derive(Debug, Clone, Copy)]
138enum FoundNode<'a> {
139 Value(&'a MonValue),
140 TypeSpec(&'a TypeSpec),
141}
142
143#[cfg(feature = "lsp")]
144fn find_node_at(value: &MonValue, position: usize) -> Option<FoundNode<'_>> {
146 if position < value.pos_start || position >= value.pos_end {
147 return None;
148 }
149
150 if let MonValueKind::Object(members) = &value.kind {
151 for member in members {
152 if let Member::Pair(pair) = member {
153 if let Some(validation) = &pair.validation {
154 if let Some(found) = find_node_in_type_spec(validation, position) {
155 return Some(found);
156 }
157 }
158 if let Some(found) = find_node_at(&pair.value, position) {
159 return Some(found);
160 }
161 }
162 }
163 }
164
165 if let MonValueKind::Array(elements) = &value.kind {
166 for element in elements {
167 if let Some(found) = find_node_at(element, position) {
168 return Some(found);
169 }
170 }
171 }
172
173 Some(FoundNode::Value(value))
174}
175
176#[cfg(feature = "lsp")]
177fn find_node_in_type_spec(type_spec: &TypeSpec, position: usize) -> Option<FoundNode<'_>> {
178 let span = type_spec.get_span();
179 if position < span.offset() || position >= span.offset() + span.len() {
180 return None;
181 }
182
183 if let TypeSpec::Collection(children, _) = type_spec {
184 for child in children {
185 if let Some(found) = find_node_in_type_spec(child, position) {
186 return Some(found);
187 }
188 }
189 }
190
191 Some(FoundNode::TypeSpec(type_spec))
192}
193
194pub fn analyze(source: &str, file_name: &str) -> Result<AnalysisResult, MonError> {
213 let mut parser = Parser::new_with_name(source, file_name.to_string())?;
214 let document = parser.parse_document()?;
215 let unresolved_document = document.clone();
216
217 let mut resolver = Resolver::new();
218 let mut path = PathBuf::from(file_name);
219 if path.is_relative() {
220 path = std::env::current_dir().unwrap().join(path);
221 }
222
223 let resolved_doc = resolver.resolve(document, source, path, None)?;
224
225 Ok(AnalysisResult {
226 document: resolved_doc,
227 unresolved_document,
228 symbol_table: resolver.symbol_table,
229 anchors: resolver.anchors,
230 })
231}
232
233impl Display for TypeSpec {
234 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
235 match self {
236 TypeSpec::Simple(name, _) => write!(f, "{name}"),
237 TypeSpec::Collection(types, _) => {
238 write!(f, "[")?;
239 for (i, t) in types.iter().enumerate() {
240 write!(f, "{t}")?;
241 if i < types.len() - 1 {
242 write!(f, ", ")?;
243 }
244 }
245 write!(f, "]")
246 }
247 TypeSpec::Spread(t, _) => write!(f, "{t}..."),
248 }
249 }
250}
251
252#[cfg(test)]
253mod tests {
254 use crate::analyze;
255
256 #[test]
257 fn test_simple_parse_to_json() {
258 let source = r#"
259 {
260 name: "My App",
261 version: 1.0,
262 is_enabled: true,
263 features: ["a", "b", "c"],
264 config: {
265 host: "localhost",
266 port: 8080.0,
267 }
268 }
269 "#;
270
271 let expected_json = serde_json::json!({
272 "name": "My App",
273 "version": 1.0,
274 "is_enabled": true,
275 "features": ["a", "b", "c"],
276 "config": {
277 "host": "localhost",
278 "port": 8080.0,
279 }
280 });
281
282 let analysis_result = analyze(source, "test.mon").unwrap();
283 let result = analysis_result.to_json().unwrap();
284 let result_json: serde_json::Value = serde_json::from_str(&result).unwrap();
285
286 assert_eq!(result_json, expected_json);
287 }
288
289 #[test]
290 fn test_analyze_semantic_info() {
291 let source = r"
292 {
293 MyType: #struct { field(String) },
294 &my_anchor: { a: 1 },
295 value: *my_anchor,
296 }
297 ";
298
299 let analysis_result = analyze(source, "test.mon").unwrap();
300
301 assert!(analysis_result.symbol_table.types.contains_key("MyType"));
303
304 assert!(analysis_result.anchors.contains_key("my_anchor"));
306 }
307
308 #[test]
309 fn test_simple_parse_to_yaml() {
310 let source = r#"
311 {
312 name: "My App",
313 version: 1.0,
314 is_enabled: true,
315 }
316 "#;
317
318 let expected_yaml = "is_enabled: true\nname: My App\nversion: 1.0\n";
319
320 let analysis_result = analyze(source, "test.mon").unwrap();
321 let result = analysis_result.to_yaml().unwrap();
322
323 assert_eq!(result, expected_yaml);
324 }
325}