Skip to main content

substrate_data/model/
value.rs

1//! The model: a tree of typed values.
2
3use std::fmt;
4
5/// A value.
6use crate::model::number::{int_to_f64, whole_to_i64};
7///
8/// The text form has no syntax of its own for the typed values (`Ref`,
9/// `Bytes`): they're written as strings (`"@file:images/logo.png"`), so
10/// every file stays JSON, and the schema of a field says what its string
11/// means. The binary form keeps the type.
12///
13/// A `Str` holds a string *as spelled*: one that starts with `@` is a
14/// typed value's spelling, read by the field's schema
15/// ([`crate::typed::classify`]), and plain text starting with `@` is kept
16/// with it doubled (`"@@everyone"`). Text and binary forms carry it as is;
17/// `Schema::write` of a `String` doubles the `@`, its read undoes it.
18#[derive(Debug, Clone, PartialEq, Default)]
19pub enum Value {
20    /// Nothing.
21    #[default]
22    Null,
23    /// `true` or `false`.
24    Bool(bool),
25    /// An integer.
26    Int(i64),
27    /// A number with a fraction or an exponent.
28    Float(f64),
29    /// A string, as spelled (a plain one's leading `@` doubled).
30    Str(String),
31    /// A list.
32    List(Vec<Value>),
33    /// A map, in file order.
34    Map(Map),
35    /// `@file:images/logo.png` — something of some kind, by id.
36    Ref(Ref),
37    /// Bytes as they are — a picture, a sound — written `@bytes:<base64>` as text.
38    Bytes(Vec<u8>),
39}
40
41/// A reference: its kind (`user`, `doc`, `file`, or any other the
42/// format names) and id (`images/logo.png`, `team:alice`). The id is
43/// opaque here; what it names and whether it exists is the format's
44/// business (a [`crate::Resolve`] checks it), and a typed field reads it
45/// as its kind's id type ([`crate::Id`]).
46#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
47pub struct Ref {
48    /// What it names: `user`, `doc`, `file`.
49    pub kind: KindName,
50    /// Which one, as written.
51    pub id: RefKey,
52}
53
54impl Ref {
55    /// `@kind:id`.
56    #[must_use]
57    pub fn new(kind: KindName, id: RefKey) -> Self {
58        Self { kind, id }
59    }
60
61    /// `kind` and `id` checked: `Ref::parse("file", "images/logo.png")`.
62    ///
63    /// # Errors
64    ///
65    /// [`crate::typed::TypedError`] when `kind` or `id` isn't one.
66    pub fn parse(kind: &str, id: &str) -> Result<Self, crate::typed::TypedError> {
67        let kind = KindName::new(kind)?;
68        let id = RefKey::new(id).map_err(|_| crate::typed::TypedError::BadId {
69            kind: Some(kind),
70            id: id.to_owned(),
71        })?;
72        Ok(Self { kind, id })
73    }
74
75    /// A typed id of kind `K` as a reference.
76    pub fn of<K: crate::Kind>(id: &K::Id) -> Self {
77        Self {
78            kind: K::kind_name(),
79            id: RefKey::spelled(&crate::schema::RefId::spell(id)),
80        }
81    }
82}
83
84impl fmt::Display for Ref {
85    /// `@kind:id`, as files write it.
86    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87        write!(f, "{}{}:{}", crate::names::TYPED, self.kind, self.id)
88    }
89}
90
91pub use super::map::Map;
92pub use super::reference::{KindName, RefKey};
93
94/// Which of a [`Value`]'s cases a value is — what messages name
95/// (`"a string"`, `"a list"`) when they say what was found.
96#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
97pub enum ValueKind {
98    /// Null.
99    Null,
100    /// A bool.
101    Bool,
102    /// An integer.
103    Int,
104    /// A float.
105    Float,
106    /// A string.
107    Str,
108    /// A list.
109    List,
110    /// A map.
111    Map,
112    /// A reference.
113    Ref,
114    /// Bytes.
115    Bytes,
116}
117
118impl fmt::Display for ValueKind {
119    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120        f.write_str(match self {
121            ValueKind::Null => "null",
122            ValueKind::Bool => "true/false",
123            ValueKind::Int => "an integer",
124            ValueKind::Float => "a number",
125            ValueKind::Str => "a string",
126            ValueKind::List => "a list",
127            ValueKind::Map => "an object",
128            ValueKind::Ref => "a reference",
129            ValueKind::Bytes => "bytes",
130        })
131    }
132}
133
134impl Value {
135    /// Which case it is.
136    pub fn kind(&self) -> ValueKind {
137        match self {
138            Value::Null => ValueKind::Null,
139            Value::Bool(_) => ValueKind::Bool,
140            Value::Int(_) => ValueKind::Int,
141            Value::Float(_) => ValueKind::Float,
142            Value::Str(_) => ValueKind::Str,
143            Value::List(_) => ValueKind::List,
144            Value::Map(_) => ValueKind::Map,
145            Value::Ref(_) => ValueKind::Ref,
146            Value::Bytes(_) => ValueKind::Bytes,
147        }
148    }
149
150    /// Whether it's null.
151    pub fn is_null(&self) -> bool {
152        matches!(self, Value::Null)
153    }
154
155    /// The bool, if it's one.
156    pub fn as_bool(&self) -> Option<bool> {
157        match self {
158            Value::Bool(b) => Some(*b),
159            _ => None,
160        }
161    }
162
163    /// The integer, if it's one (a float with no fraction is).
164    pub fn as_i64(&self) -> Option<i64> {
165        match self {
166            Value::Int(n) => Some(*n),
167            Value::Float(f) => whole_to_i64(*f),
168            _ => None,
169        }
170    }
171
172    /// The number, if it's one.
173    pub fn as_f64(&self) -> Option<f64> {
174        match self {
175            Value::Int(n) => Some(int_to_f64(*n)),
176            Value::Float(f) => Some(*f),
177            _ => None,
178        }
179    }
180
181    /// The string, if it's one.
182    pub fn as_str(&self) -> Option<&str> {
183        match self {
184            Value::Str(s) => Some(s),
185            _ => None,
186        }
187    }
188
189    /// The list, if it's one.
190    pub fn as_list(&self) -> Option<&[Value]> {
191        match self {
192            Value::List(l) => Some(l),
193            _ => None,
194        }
195    }
196
197    /// The map, if it's one.
198    pub fn as_map(&self) -> Option<&Map> {
199        match self {
200            Value::Map(m) => Some(m),
201            _ => None,
202        }
203    }
204
205    /// `self[key]` of a map, `None` for anything else.
206    pub fn get(&self, key: &str) -> Option<&Value> {
207        self.as_map()?.get(key)
208    }
209
210    /// The value at `path`.
211    pub fn at(&self, path: &Path) -> Option<&Value> {
212        let mut v = self;
213        for seg in path.segments() {
214            v = match (seg, v) {
215                (Seg::Key(k), Value::Map(m)) => m.get(k)?,
216                (Seg::Index(i), Value::List(l)) => l.get(*i)?,
217                _ => return None,
218            };
219        }
220        Some(v)
221    }
222
223    /// The value `segs` lead to, to change.
224    pub fn at_mut(&mut self, segs: &[Seg]) -> Option<&mut Value> {
225        let mut v = self;
226        for seg in segs {
227            v = match (seg, v) {
228                (Seg::Key(k), Value::Map(m)) => m.get_mut(k)?,
229                (Seg::Index(i), Value::List(l)) => l.get_mut(*i)?,
230                _ => return None,
231            };
232        }
233        Some(v)
234    }
235}
236
237impl From<bool> for Value {
238    fn from(b: bool) -> Self {
239        Value::Bool(b)
240    }
241}
242impl From<i64> for Value {
243    fn from(n: i64) -> Self {
244        Value::Int(n)
245    }
246}
247impl From<f64> for Value {
248    fn from(f: f64) -> Self {
249        Value::Float(f)
250    }
251}
252impl From<&str> for Value {
253    fn from(s: &str) -> Self {
254        Value::Str(s.to_string())
255    }
256}
257impl From<String> for Value {
258    fn from(s: String) -> Self {
259        Value::Str(s)
260    }
261}
262impl From<Vec<Value>> for Value {
263    fn from(l: Vec<Value>) -> Self {
264        Value::List(l)
265    }
266}
267impl From<Map> for Value {
268    fn from(m: Map) -> Self {
269        Value::Map(m)
270    }
271}
272
273/// One step of a [`Path`].
274#[derive(Debug, Clone, PartialEq, Eq, Hash, crate::Schema)]
275#[data(tag = "type")]
276pub enum Seg {
277    /// A map's key.
278    Key(String),
279    /// A list's index.
280    Index(usize),
281}
282
283/// Where a value is in a document: `tasks[2].parts[0].owner`.
284#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
285pub struct Path(Vec<Seg>);
286
287impl Path {
288    /// The path of the document's root.
289    #[must_use]
290    pub fn root() -> Self {
291        Self(Vec::new())
292    }
293
294    /// Its steps, from the root.
295    #[must_use]
296    pub fn segments(&self) -> &[Seg] {
297        &self.0
298    }
299
300    /// Whether it's the root.
301    #[must_use]
302    pub fn is_root(&self) -> bool {
303        self.0.is_empty()
304    }
305
306    /// One step further.
307    pub fn push(&mut self, seg: Seg) {
308        self.0.push(seg);
309    }
310
311    /// One step back.
312    pub fn pop(&mut self) {
313        self.0.pop();
314    }
315
316    /// This path, then `key`.
317    #[must_use]
318    pub fn key(&self, key: &str) -> Path {
319        let mut p = self.clone();
320        p.0.push(Seg::Key(key.to_string()));
321        p
322    }
323
324    /// `rest` under this path.
325    #[must_use]
326    pub fn then(&self, rest: &Path) -> Path {
327        let mut p = self.clone();
328        p.0.extend(rest.0.iter().cloned());
329        p
330    }
331
332    /// This path, then index `i`.
333    #[must_use]
334    pub fn index(&self, i: usize) -> Path {
335        let mut p = self.clone();
336        p.0.push(Seg::Index(i));
337        p
338    }
339}
340
341/// A path as its steps.
342impl crate::Schema for Path {
343    fn shape() -> crate::Shape {
344        Vec::<Seg>::shape()
345    }
346    fn read(v: &crate::ValueRef<'_>, cx: &mut crate::Reader<'_>) -> Option<Self> {
347        Vec::<Seg>::read(v, cx).map(Path)
348    }
349    fn write(&self) -> Value {
350        self.0.write()
351    }
352    fn pack(&self, p: &mut crate::frame::Packer) {
353        self.0.pack(p);
354    }
355    fn unpack(u: &mut crate::frame::Unpacker<'_>) -> Option<Self> {
356        Vec::<Seg>::unpack(u).map(Path)
357    }
358}
359
360impl fmt::Display for Path {
361    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
362        if self.0.is_empty() {
363            return f.write_str("(root)");
364        }
365        for (i, seg) in self.0.iter().enumerate() {
366            match seg {
367                Seg::Key(k) if is_plain_key(k) => {
368                    if i > 0 {
369                        f.write_str(".")?;
370                    }
371                    f.write_str(k)?;
372                }
373                Seg::Key(k) => write!(f, "[{k:?}]")?,
374                Seg::Index(n) => write!(f, "[{n}]")?,
375            }
376        }
377        Ok(())
378    }
379}
380
381fn is_plain_key(k: &str) -> bool {
382    let mut chars = k.chars();
383    chars
384        .next()
385        .is_some_and(|c| c.is_ascii_alphabetic() || c == '_' || c == '$')
386        && chars.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '$')
387}