Skip to main content

synx_core/
value.rs

1//! SYNX value types, metadata, and options.
2
3use std::collections::HashMap;
4
5/// SYNX value types.
6#[derive(Debug, Clone, PartialEq)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8#[cfg_attr(feature = "serde", serde(untagged))]
9pub enum Value {
10    String(std::string::String),
11    Int(i64),
12    Float(f64),
13    Bool(bool),
14    Null,
15    Array(Vec<Value>),
16    Object(HashMap<std::string::String, Value>),
17    /// Secret value — displays as [SECRET], real value accessible via as_secret()
18    Secret(std::string::String),
19}
20
21impl Value {
22    pub fn as_str(&self) -> Option<&str> {
23        match self {
24            Value::String(s) | Value::Secret(s) => Some(s),
25            _ => None,
26        }
27    }
28
29    pub fn as_int(&self) -> Option<i64> {
30        match self {
31            Value::Int(n) => Some(*n),
32            _ => None,
33        }
34    }
35
36    pub fn as_float(&self) -> Option<f64> {
37        match self {
38            Value::Float(f) => Some(*f),
39            Value::Int(n) => Some(*n as f64),
40            _ => None,
41        }
42    }
43
44    pub fn as_bool(&self) -> Option<bool> {
45        match self {
46            Value::Bool(b) => Some(*b),
47            _ => None,
48        }
49    }
50
51    pub fn as_object(&self) -> Option<&HashMap<std::string::String, Value>> {
52        match self {
53            Value::Object(m) => Some(m),
54            _ => None,
55        }
56    }
57
58    pub fn as_object_mut(&mut self) -> Option<&mut HashMap<std::string::String, Value>> {
59        match self {
60            Value::Object(m) => Some(m),
61            _ => None,
62        }
63    }
64
65    pub fn as_array(&self) -> Option<&Vec<Value>> {
66        match self {
67            Value::Array(a) => Some(a),
68            _ => None,
69        }
70    }
71
72    pub fn as_array_mut(&mut self) -> Option<&mut Vec<Value>> {
73        match self {
74            Value::Array(a) => Some(a),
75            _ => None,
76        }
77    }
78
79    pub fn as_secret(&self) -> Option<&str> {
80        match self {
81            Value::Secret(s) => Some(s),
82            _ => None,
83        }
84    }
85
86    pub fn is_null(&self) -> bool {
87        matches!(self, Value::Null)
88    }
89
90    pub fn as_number_f64(&self) -> Option<f64> {
91        match self {
92            Value::Int(n) => Some(*n as f64),
93            Value::Float(f) => Some(*f),
94            _ => None,
95        }
96    }
97}
98
99impl std::fmt::Display for Value {
100    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101        match self {
102            Value::String(s) | Value::Secret(s) => write!(f, "{}", s),
103            Value::Int(n) => write!(f, "{}", n),
104            Value::Float(fl) => {
105                let s = fl.to_string();
106                if s.contains('.') { write!(f, "{}", s) } else { write!(f, "{}.0", s) }
107            }
108            Value::Bool(b) => write!(f, "{}", b),
109            Value::Null => write!(f, "null"),
110            Value::Array(arr) => {
111                write!(f, "[")?;
112                for (i, item) in arr.iter().enumerate() {
113                    if i > 0 { write!(f, ", ")?; }
114                    write!(f, "{}", item)?;
115                }
116                write!(f, "]")
117            }
118            Value::Object(_) => write!(f, "[Object]"),
119        }
120    }
121}
122
123impl std::ops::Index<&str> for Value {
124    type Output = Value;
125    fn index(&self, key: &str) -> &Value {
126        match self {
127            Value::Object(map) => map.get(key).expect("key not found"),
128            _ => panic!("not an object"),
129        }
130    }
131}
132
133/// File mode.
134#[derive(Debug, Clone, Copy, PartialEq, Eq)]
135pub enum Mode {
136    Static,
137    Active,
138}
139
140/// Metadata for a single key (markers, args, constraints).
141#[derive(Debug, Clone, PartialEq)]
142pub struct Meta {
143    pub markers: Vec<String>,
144    pub args: Vec<String>,
145    pub type_hint: Option<String>,
146    pub constraints: Option<Constraints>,
147}
148
149/// Constraints from [min:3, max:30, required, type:int].
150#[derive(Debug, Clone, Default, PartialEq)]
151pub struct Constraints {
152    pub min: Option<f64>,
153    pub max: Option<f64>,
154    pub type_name: Option<String>,
155    pub required: bool,
156    pub readonly: bool,
157    pub pattern: Option<String>,
158    pub enum_values: Option<Vec<String>>,
159}
160
161/// Map of key → metadata for one object level.
162pub type MetaMap = HashMap<String, Meta>;
163
164/// Include directive: !include path [alias]
165#[derive(Debug, Clone)]
166pub struct IncludeDirective {
167    pub path: String,
168    pub alias: String,
169}
170
171/// Full parse result with metadata.
172#[derive(Debug)]
173pub struct ParseResult {
174    pub root: Value,
175    pub mode: Mode,
176    pub locked: bool,
177    /// Metadata for each nesting level, keyed by dot-path prefix.
178    /// "" = root level, "server" = server sub-object, etc.
179    pub metadata: HashMap<String, MetaMap>,
180    /// !include directives parsed from the file.
181    pub includes: Vec<IncludeDirective>,
182}
183
184/// Options for active mode resolution.
185#[derive(Debug, Clone, Default)]
186pub struct Options {
187    pub env: Option<HashMap<String, String>>,
188    pub region: Option<String>,
189    pub lang: Option<String>,
190    pub base_path: Option<String>,
191}