Skip to main content

substrate_data/schema/dictionary/
dictionary.rs

1//! [`Dictionary`]: a set of values by kind and id, a resolver that expands
2//! references to them.
3
4use std::collections::BTreeMap;
5
6use super::entries::Entries;
7use crate::model::borrowed::ValueRef;
8use crate::model::value::{KindName, Map, Path, Ref, RefKey, Seg, Value};
9use crate::schema::{Reader, Resolve, Schema, Shape, Unresolved, is_metadata_key};
10
11/// The values references expand to — the words of a vocabulary, the texts
12/// of a language, the entries of a table — kept per kind and id, in the
13/// order they were added. It is a [`Resolve`]: a reference of a kind it
14/// holds is checked against its ids and, where a field takes a value
15/// ([`with::Expand`](crate::with::Expand)), expanded to the value; a
16/// reference of a kind it doesn't hold is left alone.
17///
18/// It is a [`Schema`] too, written `{ <kind>: { <id>: <value>, … }, … }`, so
19/// a tool loads it from text, a value or the binary form like any
20/// document (an empty kind, `{ word: {} }`, holds nothing and says every
21/// `@word:…` is missing). Keys that are notes (`$…`, `_…`, `comment`) are
22/// skipped, as in any object.
23///
24/// ```
25/// use substrate_data::schema::{Dictionary, from_text, from_value_with};
26/// use substrate_data::{Ref, Value};
27///
28/// let mut dictionary: Dictionary =
29///     from_text(r#"{ project: { alpha: { name: "Alpha" } } }"#).unwrap().value;
30/// let alpha = Ref::parse("project", "alpha").unwrap();
31/// assert_eq!(dictionary.get(&alpha).unwrap().get("name").unwrap().as_str(), Some("Alpha"));
32///
33/// // As a resolver, it expands what a field reads through `Expand`.
34/// #[derive(substrate_data::Schema)]
35/// struct Card {
36///     #[data(with = "substrate_data::with::Expand<substrate_data::with::Plain>")]
37///     project: Value,
38/// }
39/// let doc: Value = from_text(r#"{ project: "@project:alpha" }"#).unwrap().value;
40/// let card = from_value_with::<Card>(&doc, &mut dictionary).unwrap().value;
41/// assert_eq!(card.project.get("name").unwrap().as_str(), Some("Alpha"));
42/// ```
43#[derive(Debug, Clone, Default, PartialEq)]
44pub struct Dictionary {
45    kinds: BTreeMap<KindName, Entries>,
46}
47
48impl Dictionary {
49    /// A dictionary holding nothing.
50    #[must_use]
51    pub fn new() -> Self {
52        Self::default()
53    }
54
55    /// `value` as the value of `id` of `kind`, returning the one it
56    /// replaced.
57    pub fn insert(&mut self, kind: KindName, id: RefKey, value: Value) -> Option<Value> {
58        self.kinds.entry(kind).or_default().insert(id, value)
59    }
60
61    /// Holds `kind` (with no values if there are none): its references are
62    /// now checked, and one to an id there isn't is missing.
63    pub fn declare(&mut self, kind: KindName) {
64        self.kinds.entry(kind).or_default();
65    }
66
67    /// Whether the dictionary holds `kind`.
68    #[must_use]
69    pub fn holds(&self, kind: KindName) -> bool {
70        self.kinds.contains_key(&kind)
71    }
72
73    /// The value `r` names.
74    #[must_use]
75    pub fn get(&self, r: &Ref) -> Option<&Value> {
76        self.kinds.get(&r.kind)?.get(&r.id)
77    }
78
79    /// The kinds held, by name.
80    pub fn kinds(&self) -> impl Iterator<Item = KindName> + '_ {
81        self.kinds.keys().copied()
82    }
83
84    /// `kind`'s values in the order they were added (none if it isn't held).
85    pub fn entries(&self, kind: KindName) -> impl Iterator<Item = (&RefKey, &Value)> {
86        self.kinds.get(&kind).into_iter().flat_map(Entries::iter)
87    }
88
89    /// How many values it holds, all kinds together.
90    #[must_use]
91    pub fn len(&self) -> usize {
92        self.kinds.values().map(Entries::len).sum()
93    }
94
95    /// Whether it holds no value.
96    #[must_use]
97    pub fn is_empty(&self) -> bool {
98        self.len() == 0
99    }
100}
101
102impl Resolve for Dictionary {
103    fn reference(&mut self, r: &Ref, _path: &Path) -> Result<RefKey, Unresolved> {
104        match self.kinds.get(&r.kind) {
105            Some(entries) if entries.get(&r.id).is_none() => Err(Unresolved::Missing(r.clone())),
106            _ => Ok(r.id.clone()),
107        }
108    }
109
110    fn expand(&mut self, r: &Ref, _path: &Path) -> Result<Option<Value>, Unresolved> {
111        let Some(entries) = self.kinds.get(&r.kind) else {
112            return Ok(None);
113        };
114        entries
115            .get(&r.id)
116            .cloned()
117            .map(Some)
118            .ok_or_else(|| Unresolved::Missing(r.clone()))
119    }
120}
121
122/// A kind's object read into `dictionary`; `None` if it has problems.
123fn read_kind(
124    dictionary: &mut Dictionary,
125    kind: &str,
126    v: &ValueRef<'_>,
127    cx: &mut Reader<'_>,
128) -> Option<()> {
129    let kind = KindName::new(kind)
130        .map_err(|e| cx.problem_of(crate::Problem::BadTyped, e.to_string()))
131        .ok()?;
132    let entries = cx.map(v)?;
133    dictionary.declare(kind);
134    let mut ok = true;
135    for (id, value) in entries.iter().filter(|(id, _)| !is_metadata_key(id)) {
136        let read = cx.at(Seg::Key(id.to_string()), |cx| {
137            RefKey::new(id)
138                .map_err(|e| cx.problem_of(crate::Problem::BadId, e.to_string()))
139                .ok()
140        });
141        match read {
142            Some(key) => {
143                dictionary.insert(kind, key, value.to_value());
144            }
145            None => ok = false,
146        }
147    }
148    ok.then_some(())
149}
150
151fn words() -> Shape {
152    Shape::Map(<Value as Schema>::shape)
153}
154
155impl Schema for Dictionary {
156    fn shape() -> Shape {
157        Shape::Map(words)
158    }
159    fn read(v: &ValueRef<'_>, cx: &mut Reader<'_>) -> Option<Self> {
160        let kinds = cx.map(v)?;
161        let mut out = Dictionary::new();
162        let mut ok = true;
163        for (kind, entries) in kinds.iter().filter(|(k, _)| !is_metadata_key(k)) {
164            let read = cx.at(Seg::Key(kind.to_string()), |cx| {
165                read_kind(&mut out, kind, entries, cx)
166            });
167            ok &= read.is_some();
168        }
169        ok.then_some(out)
170    }
171    fn write(&self) -> Value {
172        let kinds = self.kinds.iter().map(|(kind, entries)| {
173            let values: Map = entries
174                .iter()
175                .map(|(id, v)| (id.as_str().to_owned(), v.clone()))
176                .collect();
177            (kind.as_str().to_owned(), Value::Map(values))
178        });
179        Value::Map(kinds.collect())
180    }
181}