mon_core/
serialization.rs

1//! # MON AST to Serializable Value Conversion
2//!
3//! This module provides the logic for converting a resolved MON Abstract Syntax Tree (AST)
4//! into a generic, serializable data structure. This is the final step in the pipeline
5//! before the MON data can be output as JSON, YAML, or any other format supported by `serde`.
6//!
7//! ## Architectural Overview
8//!
9//! The process is straightforward:
10//!
11//! 1.  A fully resolved and validated [`MonValue`](crate::ast::MonValue) from the AST is passed
12//!     to the internal `to_value` function.
13//! 2.  The function recursively traverses the `MonValue`, converting it into a tree of
14//!     [`Value`] enums.
15//! 3.  During this conversion, language-specific AST nodes that are not part of the data model—such
16//!     as `TypeDefinition` members—are discarded. Only `Pair` members are included in the final object.
17//! 4.  The resulting [`Value`] is designed to be directly serializable by `serde`. It uses a
18//!     `BTreeMap` for objects to ensure that the output has a deterministic order of keys,
19//!     which is good practice for configuration and data files.
20//!
21//! ## Use Cases
22//!
23//! This module is used internally by [`AnalysisResult`](crate::api::AnalysisResult) to provide
24//! the `to_json()` and `to_yaml()` methods. Direct interaction with this module is typically not
25//! necessary for end-users, as the public API in the [`api`](crate::api) module provides a more
26//! convenient interface.
27//!
28//! ```rust
29//! use mon_core::api::analyze;
30//!
31//! # fn main() -> Result<(), mon_core::error::MonError> {
32//! let source = "{ b: 2, a: 1 }";
33//!
34//! // The serialization module is used behind the scenes by `to_json`.
35//! let result = analyze(source, "test.mon")?;
36//! let json = result.to_json().unwrap();
37//!
38//! // Note that the keys are sorted alphabetically because of the BTreeMap.
39//! assert_eq!(json, "{\n  \"a\": 1.0,\n  \"b\": 2.0\n}");
40//! # Ok(())
41//! # }
42//! ```
43use crate::ast::{Member, MonValue, MonValueKind};
44use serde::Serialize;
45use std::collections::BTreeMap;
46
47#[derive(Debug, Clone, PartialEq, Serialize)]
48#[serde(untagged)]
49pub enum Value {
50    String(String),
51    Number(f64),
52    Boolean(bool),
53    Null,
54    Array(Vec<Value>),
55    Object(BTreeMap<String, Value>),
56}
57
58pub(crate) fn to_value(mon_value: &MonValue) -> Value {
59    match &mon_value.kind {
60        MonValueKind::String(s) => Value::String(s.clone()),
61        MonValueKind::Number(n) => Value::Number(*n),
62        MonValueKind::Boolean(b) => Value::Boolean(*b),
63        MonValueKind::Array(arr) => Value::Array(arr.iter().map(to_value).collect()),
64        MonValueKind::Object(obj) => {
65            let mut map = BTreeMap::new();
66            for member in obj {
67                if let Member::Pair(pair) = member {
68                    // We only include pairs in the final JSON output.
69                    // Type definitions, anchors, etc., are not part of the data.
70                    map.insert(pair.key.clone(), to_value(&pair.value));
71                }
72            }
73            Value::Object(map)
74        }
75        // Aliases, Spreads, etc., should be resolved by this point.
76        // If we encounter them here, it's a logic error in the resolver.
77        MonValueKind::Null
78        | MonValueKind::Alias(_)
79        | MonValueKind::EnumValue { .. }
80        | MonValueKind::ArraySpread(_) => Value::Null, // Or panic, depending on desired strictness.
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87    use crate::ast::{Member, MonValue, MonValueKind, Pair};
88    use std::collections::BTreeMap;
89
90    fn make_value(kind: MonValueKind) -> MonValue {
91        MonValue {
92            kind,
93            anchor: None,
94            pos_start: 0,
95            pos_end: 0,
96        }
97    }
98
99    #[test]
100    fn test_string_conversion() {
101        let mon_val = make_value(MonValueKind::String("hello".to_string()));
102        let result = to_value(&mon_val);
103        assert_eq!(result, Value::String("hello".to_string()));
104    }
105
106    #[test]
107    fn test_number_conversion() {
108        let mon_val = make_value(MonValueKind::Number(42.5));
109        let result = to_value(&mon_val);
110        assert_eq!(result, Value::Number(42.5));
111    }
112
113    #[test]
114    fn test_boolean_conversion() {
115        let mon_val = make_value(MonValueKind::Boolean(true));
116        assert_eq!(to_value(&mon_val), Value::Boolean(true));
117
118        let mon_val2 = make_value(MonValueKind::Boolean(false));
119        assert_eq!(to_value(&mon_val2), Value::Boolean(false));
120    }
121
122    #[test]
123    fn test_null_conversion() {
124        let mon_val = make_value(MonValueKind::Null);
125        assert_eq!(to_value(&mon_val), Value::Null);
126    }
127
128    #[test]
129    fn test_array_conversion() {
130        let arr = vec![
131            make_value(MonValueKind::Number(1.0)),
132            make_value(MonValueKind::Number(2.0)),
133            make_value(MonValueKind::Number(3.0)),
134        ];
135        let mon_val = make_value(MonValueKind::Array(arr));
136        let result = to_value(&mon_val);
137
138        assert_eq!(
139            result,
140            Value::Array(vec![
141                Value::Number(1.0),
142                Value::Number(2.0),
143                Value::Number(3.0),
144            ])
145        );
146    }
147
148    #[test]
149    fn test_object_conversion() {
150        let pair = Pair {
151            key: "test".to_string(),
152            value: make_value(MonValueKind::String("value".to_string())),
153            validation: None,
154        };
155        let obj = vec![Member::Pair(pair)];
156        let mon_val = make_value(MonValueKind::Object(obj));
157        let result = to_value(&mon_val);
158
159        let mut expected_map = BTreeMap::new();
160        expected_map.insert("test".to_string(), Value::String("value".to_string()));
161        assert_eq!(result, Value::Object(expected_map));
162    }
163
164    #[test]
165    fn test_object_excludes_non_pair_members() {
166        let pair = Pair {
167            key: "data".to_string(),
168            value: make_value(MonValueKind::Number(123.0)),
169            validation: None,
170        };
171        let obj = vec![Member::Pair(pair), Member::Spread("ignored".to_string())];
172        let mon_val = make_value(MonValueKind::Object(obj));
173        let result = to_value(&mon_val);
174
175        let mut expected_map = BTreeMap::new();
176        expected_map.insert("data".to_string(), Value::Number(123.0));
177        assert_eq!(result, Value::Object(expected_map));
178    }
179
180    #[test]
181    fn test_alias_converts_to_null() {
182        let mon_val = make_value(MonValueKind::Alias("some_anchor".to_string()));
183        assert_eq!(to_value(&mon_val), Value::Null);
184    }
185
186    #[test]
187    fn test_enum_value_converts_to_null() {
188        let mon_val = make_value(MonValueKind::EnumValue {
189            enum_name: "Status".to_string(),
190            variant_name: "Active".to_string(),
191        });
192        assert_eq!(to_value(&mon_val), Value::Null);
193    }
194
195    #[test]
196    fn test_array_spread_converts_to_null() {
197        let mon_val = make_value(MonValueKind::ArraySpread("anchor".to_string()));
198        assert_eq!(to_value(&mon_val), Value::Null);
199    }
200
201    #[test]
202    fn test_nested_object() {
203        let inner_pair = Pair {
204            key: "inner".to_string(),
205            value: make_value(MonValueKind::Number(42.0)),
206            validation: None,
207        };
208        let inner_obj = vec![Member::Pair(inner_pair)];
209        let outer_pair = Pair {
210            key: "outer".to_string(),
211            value: make_value(MonValueKind::Object(inner_obj)),
212            validation: None,
213        };
214        let outer_obj = vec![Member::Pair(outer_pair)];
215        let mon_val = make_value(MonValueKind::Object(outer_obj));
216
217        let result = to_value(&mon_val);
218
219        let mut inner_map = BTreeMap::new();
220        inner_map.insert("inner".to_string(), Value::Number(42.0));
221        let mut outer_map = BTreeMap::new();
222        outer_map.insert("outer".to_string(), Value::Object(inner_map));
223
224        assert_eq!(result, Value::Object(outer_map));
225    }
226}