1#[allow(dead_code)]
47use crate::ast::{MonDocument, MonValue, SymbolTable, TypeSpec};
48use crate::error::MonError;
49
50#[cfg(feature = "lsp")]
51use crate::ast::{Member, MonValueKind};
52#[cfg(feature = "lsp")]
53use crate::lsp;
54use crate::parser::Parser;
55use crate::resolver::Resolver;
56use crate::serialization::{to_value, Value};
57#[cfg(feature = "lsp")]
58use miette::SourceSpan;
59use serde::{Serialize, Serializer};
60use serde_json;
61use serde_yaml;
62use std::collections::HashMap;
63use std::fmt::Display;
64use std::path::PathBuf;
65
66pub struct AnalysisResult {
72 pub document: MonDocument,
74 pub unresolved_document: MonDocument,
76 pub symbol_table: SymbolTable,
78 pub anchors: HashMap<String, MonValue>,
80}
81
82impl Serialize for AnalysisResult {
83 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
84 where
85 S: Serializer,
86 {
87 let value = self.to_value();
88 value.serialize(serializer)
89 }
90}
91
92impl AnalysisResult {
93 #[must_use]
95 pub fn to_value(&self) -> Value {
96 to_value(&self.document.root)
97 }
98
99 pub fn to_json(&self) -> Result<String, serde_json::Error> {
104 serde_json::to_string_pretty(&self)
105 }
106
107 pub fn to_yaml(&self) -> Result<String, serde_yaml::Error> {
112 serde_yaml::to_string(&self)
113 }
114
115 #[cfg(feature = "lsp")]
116 #[must_use]
119 pub fn get_definition_at(&self, position: usize) -> Option<SourceSpan> {
120 let node = find_node_at(&self.unresolved_document.root, position)?;
121
122 match node {
123 FoundNode::Value(value) => match &value.kind {
124 MonValueKind::Alias(alias_name) => {
125 let anchor_def = self.anchors.get(alias_name)?;
126 Some(anchor_def.get_source_span())
127 }
128 _ => None,
129 },
130 FoundNode::TypeSpec(type_spec) => match type_spec {
131 TypeSpec::Simple(name, _) => {
132 let type_def = self.symbol_table.types.get(name)?;
133 Some(type_def.def_type.get_span())
134 }
135 _ => None,
136 },
137 }
138 }
139
140 #[cfg(feature = "lsp")]
141 #[must_use]
144 pub fn get_type_info_at(&self, position: usize) -> Option<String> {
145 let symbol_info = lsp::find_symbol_at(&self.unresolved_document.root, position)?;
146
147 if let Some(validation) = symbol_info.validation {
148 return Some(validation.to_string());
149 }
150
151 match symbol_info.node {
152 lsp::FoundNode::Value(value) => Some(value.kind.to_string()),
153 lsp::FoundNode::TypeSpec(type_spec) => Some(type_spec.to_string()),
154 }
155 }
156 #[cfg(feature = "lsp")]
157 pub fn find_references(&self, position: usize) -> Option<Vec<SourceSpan>> {
159 let symbol_info = lsp::find_symbol_at(&self.unresolved_document.root, position)?;
160
161 let (name_to_find, definition_span) = match symbol_info.node {
162 lsp::FoundNode::Value(value) => match &value.kind {
163 MonValueKind::Alias(alias_name) => {
164 let anchor_def = self.anchors.get(alias_name)?;
165 (alias_name.clone(), anchor_def.get_source_span())
166 }
167 _ => return None,
168 },
169 lsp::FoundNode::TypeSpec(type_spec) => match type_spec {
170 TypeSpec::Simple(name, _) => {
171 let type_def = self.symbol_table.types.get(name)?;
172 (name.clone(), type_def.name_span)
173 }
174 _ => return None,
175 },
176 };
177
178 let usages = lsp::find_all_usages(&self.unresolved_document.root, &name_to_find)
179 .into_iter()
180 .filter(|span| *span != definition_span)
181 .collect();
182 Some(usages)
183 }
184}
185
186#[cfg(feature = "lsp")]
187#[derive(Debug, Clone, Copy)]
188enum FoundNode<'a> {
190 Value(&'a MonValue),
191 TypeSpec(&'a TypeSpec),
192}
193
194#[cfg(feature = "lsp")]
195fn find_node_at(value: &MonValue, position: usize) -> Option<FoundNode<'_>> {
197 if position < value.pos_start || position >= value.pos_end {
198 return None;
199 }
200
201 if let MonValueKind::Object(members) = &value.kind {
202 for member in members {
203 if let Member::Pair(pair) = member {
204 if let Some(validation) = &pair.validation {
205 if let Some(found) = find_node_in_type_spec(validation, position) {
206 return Some(found);
207 }
208 }
209 if let Some(found) = find_node_at(&pair.value, position) {
210 return Some(found);
211 }
212 }
213 }
214 }
215
216 if let MonValueKind::Array(elements) = &value.kind {
217 for element in elements {
218 if let Some(found) = find_node_at(element, position) {
219 return Some(found);
220 }
221 }
222 }
223
224 Some(FoundNode::Value(value))
225}
226
227#[cfg(feature = "lsp")]
228fn find_node_in_type_spec(type_spec: &TypeSpec, position: usize) -> Option<FoundNode<'_>> {
230 let span = type_spec.get_span();
231 if position < span.offset() || position >= span.offset() + span.len() {
232 return None;
233 }
234
235 if let TypeSpec::Collection(children, _) = type_spec {
236 for child in children {
237 if let Some(found) = find_node_in_type_spec(child, position) {
238 return Some(found);
239 }
240 }
241 }
242
243 Some(FoundNode::TypeSpec(type_spec))
244}
245
246pub fn analyze(source: &str, file_name: &str) -> Result<AnalysisResult, MonError> {
265 let mut parser = Parser::new_with_name(source, file_name.to_string())?;
266 let document = parser.parse_document()?;
267 let unresolved_document = document.clone();
268
269 let mut resolver = Resolver::new();
270 let mut path = PathBuf::from(file_name);
271 if path.is_relative() {
272 path = std::env::current_dir().unwrap().join(path);
273 }
274
275 let resolved_doc = resolver.resolve(document, source, path, None)?;
276
277 Ok(AnalysisResult {
278 document: resolved_doc,
279 unresolved_document,
280 symbol_table: resolver.symbol_table,
281 anchors: resolver.anchors,
282 })
283}
284
285impl Display for TypeSpec {
286 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
287 match self {
288 TypeSpec::Simple(name, _) => write!(f, "{name}"),
289 TypeSpec::Collection(types, _) => {
290 write!(f, "[")?;
291 for (i, t) in types.iter().enumerate() {
292 write!(f, "{t}")?;
293 if i < types.len() - 1 {
294 write!(f, ", ")?;
295 }
296 }
297 write!(f, "]")
298 }
299 TypeSpec::Spread(t, _) => write!(f, "{t}..."),
300 }
301 }
302}
303
304#[cfg(test)]
305mod tests {
306 use crate::analyze;
307
308 #[test]
309 fn test_simple_parse_to_json() {
310 let source = r#"{
311 name: "My App",
312 version: 1.0,
313 is_enabled: true,
314 features: ["a", "b", "c"],
315 config: {
316 host: "localhost",
317 port: 8080.0,
318 }
319 }"#;
320
321 let expected_json = serde_json::json!({
322 "name": "My App",
323 "version": 1.0,
324 "is_enabled": true,
325 "features": ["a", "b", "c"],
326 "config": {
327 "host": "localhost",
328 "port": 8080.0,
329 }
330 });
331
332 let analysis_result = analyze(source, "test.mon").unwrap();
333 let result = analysis_result.to_json().unwrap();
334 let result_json: serde_json::Value = serde_json::from_str(&result).unwrap();
335
336 assert_eq!(result_json, expected_json);
337 }
338
339 #[test]
340 fn test_analyze_semantic_info() {
341 let source = r#"{
342 MyType: #struct { field(String) },
343 &my_anchor: { a: 1 },
344 value: *my_anchor,
345 }"#;
346
347 let analysis_result = analyze(source, "test.mon").unwrap();
348
349 assert!(analysis_result.symbol_table.types.contains_key("MyType"));
351
352 assert!(analysis_result.anchors.contains_key("my_anchor"));
354 }
355
356 #[test]
357 fn test_simple_parse_to_yaml() {
358 let source = r#"
359{
360 name: "My App",
361 version: 1.0,
362 is_enabled: true,
363}"#;
364
365 let expected_yaml = "is_enabled: true\nname: My App\nversion: 1.0\n";
366
367 let analysis_result = analyze(source, "test.mon").unwrap();
368 let result = analysis_result.to_yaml().unwrap();
369
370 assert_eq!(result, expected_yaml);
371 }
372}