Skip to main content

eure_document/
value.rs

1use num_bigint::BigInt;
2
3use crate::{identifier::Identifier, prelude_internal::*, text::Text};
4
5#[derive(Debug, Clone, PartialEq, Copy)]
6pub enum ValueKind {
7    Hole,
8    Null,
9    Bool,
10    Integer,
11    F32,
12    F64,
13    Text,
14    Array,
15    Tuple,
16    Map,
17    PartialMap,
18}
19
20impl core::fmt::Display for ValueKind {
21    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
22        match self {
23            Self::Hole => write!(f, "hole"),
24            Self::Null => write!(f, "null"),
25            Self::Bool => write!(f, "bool"),
26            Self::Integer => write!(f, "integer"),
27            Self::F32 => write!(f, "f32"),
28            Self::F64 => write!(f, "f64"),
29            Self::Text => write!(f, "text"),
30            Self::Array => write!(f, "array"),
31            Self::Tuple => write!(f, "tuple"),
32            Self::Map => write!(f, "map"),
33            Self::PartialMap => write!(f, "partial-map"),
34        }
35    }
36}
37
38#[derive(Debug, Clone, PartialEq)]
39pub enum PrimitiveValue {
40    Null,
41    Bool(bool),
42    Integer(BigInt),
43    F32(f32),
44    F64(f64),
45    /// Unified text type for strings and code.
46    ///
47    /// - `"..."` syntax produces `Text` with `Language::Plaintext`
48    /// - `` `...` `` syntax produces `Text` with `Language::Implicit`
49    /// - `` lang`...` `` syntax produces `Text` with `Language::Other(lang)`
50    Text(Text),
51}
52
53impl PrimitiveValue {
54    /// Returns the text if this is a `Text` variant.
55    pub fn as_text(&self) -> Option<&Text> {
56        if let Self::Text(text) = self {
57            Some(text)
58        } else {
59            None
60        }
61    }
62
63    /// Returns the text content as a string slice if this is a `Text` variant.
64    pub fn as_str(&self) -> Option<&str> {
65        self.as_text().map(|t| t.as_str())
66    }
67
68    pub(crate) fn kind(&self) -> ValueKind {
69        match self {
70            Self::Null => ValueKind::Null,
71            Self::Bool(_) => ValueKind::Bool,
72            Self::Integer(_) => ValueKind::Integer,
73            Self::F32(_) => ValueKind::F32,
74            Self::F64(_) => ValueKind::F64,
75            Self::Text(_) => ValueKind::Text,
76        }
77    }
78}
79
80#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
81/// Key-comparable value which implements `Eq` and `Hash`.
82///
83/// Eure restricts map keys to three types — `String`, `Number` (BigInt),
84/// and `Tuple<ObjectKey>` — for practical and predictable behavior.
85///
86/// - **Deterministic equality:**
87///   These types provide stable, well-defined equality and hashing.
88///   Types like floats, null, or holes introduce ambiguous or
89///   platform-dependent comparison rules.
90///
91/// - **Reliable round-tripping:**
92///   Keys must serialize and deserialize without losing meaning.
93///   Strings, integers, and tuples have canonical and unambiguous textual forms.
94///
95/// - **Tooling-friendly:**
96///   This set balances expressiveness and simplicity, making keys easy
97///   to validate, index, and reason about across implementations.
98///
99/// Note: In key position, `true`, `false`, and `null` are parsed as string
100/// identifiers, not as boolean/null values. For example, `a.true = true`
101/// creates a key `"true"` with boolean value `true`.
102pub enum ObjectKey {
103    Number(BigInt),
104    String(String),
105    Tuple(Tuple<ObjectKey>),
106}
107
108impl core::fmt::Display for ObjectKey {
109    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
110        match self {
111            ObjectKey::Number(n) => write!(f, "{}", n),
112            ObjectKey::String(s) => {
113                write!(f, "\"")?;
114                for c in s.chars() {
115                    match c {
116                        '"' => write!(f, "\\\"")?,
117                        '\\' => write!(f, "\\\\")?,
118                        _ => write!(f, "{}", c)?,
119                    }
120                }
121                write!(f, "\"")
122            }
123            ObjectKey::Tuple(t) => write!(f, "{}", t),
124        }
125    }
126}
127
128impl From<&str> for ObjectKey {
129    fn from(s: &str) -> Self {
130        ObjectKey::String(s.to_string())
131    }
132}
133
134impl From<String> for ObjectKey {
135    fn from(s: String) -> Self {
136        ObjectKey::String(s)
137    }
138}
139
140impl From<bool> for ObjectKey {
141    fn from(b: bool) -> Self {
142        ObjectKey::String(if b { "true" } else { "false" }.to_string())
143    }
144}
145
146macro_rules! impl_from_int_for_object_key {
147    ($($ty:ty),*) => {
148        $(
149            impl From<$ty> for ObjectKey {
150                fn from(n: $ty) -> Self {
151                    ObjectKey::Number(BigInt::from(n))
152                }
153            }
154        )*
155    };
156}
157
158impl_from_int_for_object_key!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize);
159
160impl From<BigInt> for ObjectKey {
161    fn from(n: BigInt) -> Self {
162        ObjectKey::Number(n)
163    }
164}
165
166/// A key that may be a hole (unresolved placeholder) or a fully resolved key.
167///
168/// This is a superset of [`ObjectKey`] used exclusively in [`PartialMap`] nodes.
169///
170/// # Equality Semantics
171/// Equality is label-based: `Hole(Some("a")) == Hole(Some("a"))` is true,
172/// enabling structural document comparison (e.g., in `assert_eq!`).
173/// This is **syntactic** equality only — it does not imply semantic interchangeability.
174///
175/// Anonymous holes (`Hole(None)`) still compare equal for structural equality,
176/// but lookup operations treat them as unique placeholders that never deduplicate.
177/// Labeled holes (`Hole(Some(label))`) are deduplicated by label.
178#[derive(Debug, Clone, PartialEq, Eq, Hash)]
179pub enum PartialObjectKey {
180    Number(BigInt),
181    String(String),
182    /// An unresolved hole key: `!` (None) or `!label` (Some(label)).
183    Hole(Option<Identifier>),
184    /// A tuple key that may contain holes.
185    Tuple(Tuple<PartialObjectKey>),
186}
187
188impl PartialObjectKey {
189    /// Returns true when this key contains an anonymous hole anywhere within it.
190    ///
191    /// Anonymous holes are syntactically comparable, but they are never
192    /// deduplicated when looking up PartialMap entries.
193    pub fn contains_anonymous_hole(&self) -> bool {
194        match self {
195            PartialObjectKey::Hole(None) => true,
196            PartialObjectKey::Hole(Some(_))
197            | PartialObjectKey::Number(_)
198            | PartialObjectKey::String(_) => false,
199            PartialObjectKey::Tuple(items) => items.iter().any(Self::contains_anonymous_hole),
200        }
201    }
202}
203
204impl core::fmt::Display for PartialObjectKey {
205    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
206        match self {
207            PartialObjectKey::Number(n) => write!(f, "{}", n),
208            PartialObjectKey::String(s) => {
209                write!(f, "\"")?;
210                for c in s.chars() {
211                    match c {
212                        '"' => write!(f, "\\\"")?,
213                        '\\' => write!(f, "\\\\")?,
214                        _ => write!(f, "{}", c)?,
215                    }
216                }
217                write!(f, "\"")
218            }
219            PartialObjectKey::Hole(None) => write!(f, "!"),
220            PartialObjectKey::Hole(Some(label)) => write!(f, "!{}", label),
221            PartialObjectKey::Tuple(t) => write!(f, "{}", t),
222        }
223    }
224}
225
226impl From<ObjectKey> for PartialObjectKey {
227    fn from(key: ObjectKey) -> Self {
228        match key {
229            ObjectKey::Number(n) => PartialObjectKey::Number(n),
230            ObjectKey::String(s) => PartialObjectKey::String(s),
231            ObjectKey::Tuple(t) => PartialObjectKey::Tuple(Tuple(
232                t.0.into_iter().map(PartialObjectKey::from).collect(),
233            )),
234        }
235    }
236}
237
238/// Error returned when a [`PartialObjectKey`] contains a hole and cannot be converted to [`ObjectKey`].
239#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
240#[error("PartialObjectKey contains a hole and cannot be converted to ObjectKey")]
241pub struct HoleKeyError;
242
243impl TryFrom<PartialObjectKey> for ObjectKey {
244    type Error = HoleKeyError;
245
246    fn try_from(key: PartialObjectKey) -> Result<Self, Self::Error> {
247        match key {
248            PartialObjectKey::Number(n) => Ok(ObjectKey::Number(n)),
249            PartialObjectKey::String(s) => Ok(ObjectKey::String(s)),
250            PartialObjectKey::Hole(_) => Err(HoleKeyError),
251            PartialObjectKey::Tuple(t) => {
252                let keys: Result<Vec<ObjectKey>, _> =
253                    t.0.into_iter().map(ObjectKey::try_from).collect();
254                Ok(ObjectKey::Tuple(Tuple(keys?)))
255            }
256        }
257    }
258}
259
260#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Plural, Default)]
261pub struct Tuple<T>(pub Vec<T>);
262
263impl core::fmt::Display for Tuple<ObjectKey> {
264    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
265        write!(f, "(")?;
266        for (i, item) in self.0.iter().enumerate() {
267            if i != 0 {
268                write!(f, ", ")?;
269            }
270            write!(f, "{}", item)?;
271        }
272        write!(f, ")")
273    }
274}
275
276impl core::fmt::Display for Tuple<PartialObjectKey> {
277    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
278        write!(f, "(")?;
279        for (i, item) in self.0.iter().enumerate() {
280            if i != 0 {
281                write!(f, ", ")?;
282            }
283            write!(f, "{}", item)?;
284        }
285        write!(f, ")")
286    }
287}
288
289// ============================================================================
290// From implementations for PrimitiveValue
291// ============================================================================
292
293impl From<bool> for PrimitiveValue {
294    fn from(b: bool) -> Self {
295        PrimitiveValue::Bool(b)
296    }
297}
298
299macro_rules! impl_from_int_for_primitive_value {
300    ($($ty:ty),*) => {
301        $(
302            impl From<$ty> for PrimitiveValue {
303                fn from(n: $ty) -> Self {
304                    PrimitiveValue::Integer(BigInt::from(n))
305                }
306            }
307        )*
308    };
309}
310
311impl_from_int_for_primitive_value!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize);
312
313impl From<f32> for PrimitiveValue {
314    fn from(n: f32) -> Self {
315        PrimitiveValue::F32(n)
316    }
317}
318
319impl From<f64> for PrimitiveValue {
320    fn from(n: f64) -> Self {
321        PrimitiveValue::F64(n)
322    }
323}
324
325impl From<&str> for PrimitiveValue {
326    fn from(s: &str) -> Self {
327        PrimitiveValue::Text(Text::plaintext(s))
328    }
329}
330
331impl From<String> for PrimitiveValue {
332    fn from(s: String) -> Self {
333        PrimitiveValue::Text(Text::plaintext(s))
334    }
335}
336
337impl From<Text> for PrimitiveValue {
338    fn from(t: Text) -> Self {
339        PrimitiveValue::Text(t)
340    }
341}