Skip to main content

sas/
value.rs

1use std::fmt;
2
3/// Represents any SAS 1.1 value.
4///
5/// Insertion order is preserved for objects via the `Object` type.
6#[derive(Debug, Clone, PartialEq)]
7pub enum Value {
8    Null,
9    Bool(bool),
10    Int(i64),
11    Float(f64),
12    String(String),
13    Array(Vec<Value>),
14    Object(Object),
15}
16
17impl Value {
18    pub fn is_scalar(&self) -> bool {
19        matches!(self, Value::Null | Value::Bool(_) | Value::Int(_) | Value::Float(_) | Value::String(_))
20    }
21
22    pub fn as_object(&self) -> Option<&Object> {
23        if let Value::Object(o) = self { Some(o) } else { None }
24    }
25
26    pub fn as_array(&self) -> Option<&Vec<Value>> {
27        if let Value::Array(a) = self { Some(a) } else { None }
28    }
29
30    pub fn as_str(&self) -> Option<&str> {
31        if let Value::String(s) = self { Some(s) } else { None }
32    }
33
34    pub fn as_i64(&self) -> Option<i64> {
35        if let Value::Int(n) = self { Some(*n) } else { None }
36    }
37
38    pub fn as_f64(&self) -> Option<f64> {
39        match self {
40            Value::Float(f) => Some(*f),
41            Value::Int(n)   => Some(*n as f64),
42            _ => None,
43        }
44    }
45}
46
47impl fmt::Display for Value {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        match self {
50            Value::Null        => write!(f, "null"),
51            Value::Bool(b)     => write!(f, "{}", b),
52            Value::Int(n)      => write!(f, "{}", n),
53            Value::Float(n)    => write!(f, "{}", n),
54            Value::String(s)   => write!(f, "{:?}", s),
55            Value::Array(arr)  => {
56                write!(f, "[")?;
57                for (i, v) in arr.iter().enumerate() {
58                    if i > 0 { write!(f, ", ")?; }
59                    write!(f, "{}", v)?;
60                }
61                write!(f, "]")
62            }
63            Value::Object(obj) => write!(f, "{}", obj),
64        }
65    }
66}
67
68// ── Object — insertion-order-preserving map ───────────────────────────────────
69
70/// An ordered key-value store used for SAS objects.
71/// Preserves insertion order, which is required for deterministic round-trips.
72#[derive(Debug, Clone, PartialEq, Default)]
73pub struct Object {
74    pub keys: Vec<String>,
75    pub values: std::collections::HashMap<String, Value>,
76}
77
78impl Object {
79    pub fn new() -> Self {
80        Self::default()
81    }
82
83    /// Insert a key-value pair. Returns `false` if the key already exists (E01).
84    pub fn insert(&mut self, key: String, val: Value) -> bool {
85        if self.values.contains_key(&key) {
86            return false;
87        }
88        self.keys.push(key.clone());
89        self.values.insert(key, val);
90        true
91    }
92
93    pub fn get(&self, key: &str) -> Option<&Value> {
94        self.values.get(key)
95    }
96
97    pub fn contains_key(&self, key: &str) -> bool {
98        self.values.contains_key(key)
99    }
100
101    pub fn is_empty(&self) -> bool {
102        self.keys.is_empty()
103    }
104
105    pub fn len(&self) -> usize {
106        self.keys.len()
107    }
108
109    /// Iterate in insertion order.
110    pub fn iter(&self) -> impl Iterator<Item = (&str, &Value)> {
111        self.keys.iter().map(move |k| (k.as_str(), &self.values[k]))
112    }
113}
114
115impl fmt::Display for Object {
116    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117        write!(f, "{{")?;
118        for (i, k) in self.keys.iter().enumerate() {
119            if i > 0 { write!(f, ", ")?; }
120            write!(f, "{:?}: {}", k, self.values[k])?;
121        }
122        write!(f, "}}")
123    }
124}