Skip to main content

yog_data/
value.rs

1//! The model: a tree of typed values, like NBT.
2
3use std::fmt;
4
5/// A Yog Data value.
6///
7/// The text form has no syntax of its own for the typed values (`Ref`,
8/// `Tag`, `Res`, `Text`, `Image`, `Color`): they're written as strings
9/// (`"@item:minecraft:stick"`), so every file stays JSON, and the schema of
10/// a field says which type its string is. The binary form keeps the type.
11#[derive(Debug, Clone, PartialEq, Default)]
12pub enum Value {
13    #[default]
14    Null,
15    Bool(bool),
16    Int(i64),
17    Float(f64),
18    Str(String),
19    List(Vec<Value>),
20    Map(Map),
21    /// `@item:minecraft:stick` — content of some kind, by id.
22    Ref(Ref),
23    /// `@tag:item:minecraft:planks` — a tag of content of some kind.
24    Tag(Ref),
25    /// `@Strings.title` — a mod resource (`.yres` entry).
26    Res(Res),
27    /// `@lang:gui.done` — a translation key of the game's or a mod's lang files.
28    Lang(String),
29    /// Yog text: `{b|bold} and {#8d6acc|color}`.
30    Text(String),
31    /// An image source: `sprite:button`, `mymod:gui/logo`, `item:minecraft:stick`.
32    Image(String),
33    /// `0xAARRGGBB`.
34    Color(u32),
35    /// Bytes as they are — a picture, a sound — written `@bytes:<base64>` as text.
36    Bytes(Vec<u8>),
37}
38
39/// A reference to content: its kind (`item`, `block`, `entity`, `recipe`,
40/// `entry`, `category`, or a mod's own) and id (`minecraft:stick`,
41/// `minecraft:furnace[facing=north]`).
42#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
43pub struct Ref {
44    pub kind: String,
45    pub id: String,
46}
47
48impl Ref {
49    pub fn new(kind: impl Into<String>, id: impl Into<String>) -> Self {
50        Self {
51            kind: kind.into(),
52            id: id.into(),
53        }
54    }
55
56    /// The namespace of the id (`minecraft` for an id without one).
57    pub fn namespace(&self) -> &str {
58        yog_ids::split(&self.id).0
59    }
60}
61
62/// A mod resource: `@<set>.<key>` — `@Strings.screen.title` is set
63/// `Strings`, key `screen.title`.
64#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
65pub struct Res {
66    pub set: String,
67    pub key: String,
68}
69
70impl Res {
71    /// `Set.key`: what the resources list it by.
72    pub fn name(&self) -> String {
73        format!("{}.{}", self.set, self.key)
74    }
75}
76
77impl std::fmt::Display for Res {
78    /// `@Set.key`, as files write it.
79    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80        write!(f, "{}{}.{}", yog_ids::sep::TYPED, self.set, self.key)
81    }
82}
83
84/// A map: entries in file order, keys unique. Keys are shared: a decoded
85/// document allocates each distinct key once, however many maps have it.
86#[derive(Debug, Clone, PartialEq, Default)]
87pub struct Map(Vec<(yog_sync::Arc<str>, Value)>);
88
89impl Map {
90    pub fn new() -> Self {
91        Self(Vec::new())
92    }
93
94    pub fn with_capacity(n: usize) -> Self {
95        Self(Vec::with_capacity(n))
96    }
97
98    pub fn get(&self, key: &str) -> Option<&Value> {
99        self.0.iter().find(|(k, _)| &**k == key).map(|(_, v)| v)
100    }
101
102    pub fn get_mut(&mut self, key: &str) -> Option<&mut Value> {
103        self.0.iter_mut().find(|(k, _)| &**k == key).map(|(_, v)| v)
104    }
105
106    /// The value of the `i`-th entry.
107    pub fn value_at(&self, i: usize) -> Option<&Value> {
108        self.0.get(i).map(|(_, v)| v)
109    }
110
111    /// The position of `key` among the entries.
112    pub fn index_of(&self, key: &str) -> Option<usize> {
113        self.0.iter().position(|(k, _)| &**k == key)
114    }
115
116    /// Sets `key`, in place if it's there, else at the end; returns the old value.
117    pub fn insert(&mut self, key: impl Into<String>, value: Value) -> Option<Value> {
118        let key = key.into();
119        match self.get_mut(&key) {
120            Some(slot) => Some(std::mem::replace(slot, value)),
121            None => {
122                self.0.push((key.into(), value));
123                None
124            }
125        }
126    }
127
128    /// Appends without checking for the key — for decoders that already did.
129    pub(crate) fn push_unchecked(&mut self, key: yog_sync::Arc<str>, value: Value) {
130        self.0.push((key, value));
131    }
132
133    pub fn remove(&mut self, key: &str) -> Option<Value> {
134        let i = self.index_of(key)?;
135        Some(self.0.remove(i).1)
136    }
137
138    pub fn len(&self) -> usize {
139        self.0.len()
140    }
141
142    pub fn is_empty(&self) -> bool {
143        self.0.is_empty()
144    }
145
146    pub fn iter(&self) -> impl ExactSizeIterator<Item = (&str, &Value)> {
147        self.0.iter().map(|(k, v)| (&**k, v))
148    }
149
150    pub fn keys(&self) -> impl ExactSizeIterator<Item = &str> {
151        self.0.iter().map(|(k, _)| &**k)
152    }
153
154    pub fn values(&self) -> impl ExactSizeIterator<Item = &Value> {
155        self.0.iter().map(|(_, v)| v)
156    }
157
158    pub fn retain(&mut self, mut keep: impl FnMut(&str, &Value) -> bool) {
159        self.0.retain(|(k, v)| keep(k, v));
160    }
161}
162
163impl IntoIterator for Map {
164    type Item = (String, Value);
165    type IntoIter = std::iter::Map<
166        std::vec::IntoIter<(yog_sync::Arc<str>, Value)>,
167        fn((yog_sync::Arc<str>, Value)) -> (String, Value),
168    >;
169    fn into_iter(self) -> Self::IntoIter {
170        self.0.into_iter().map(|(k, v)| (k.to_string(), v))
171    }
172}
173
174impl<'a> IntoIterator for &'a Map {
175    type Item = (&'a str, &'a Value);
176    type IntoIter = std::iter::Map<
177        std::slice::Iter<'a, (yog_sync::Arc<str>, Value)>,
178        fn(&'a (yog_sync::Arc<str>, Value)) -> (&'a str, &'a Value),
179    >;
180    fn into_iter(self) -> Self::IntoIter {
181        self.0.iter().map(|(k, v)| (&**k, v))
182    }
183}
184
185impl<K: Into<String>> FromIterator<(K, Value)> for Map {
186    fn from_iter<I: IntoIterator<Item = (K, Value)>>(iter: I) -> Self {
187        let mut map = Map::new();
188        for (k, v) in iter {
189            map.insert(k, v);
190        }
191        map
192    }
193}
194
195impl Value {
196    /// The type's name, for messages: `"a string"`, `"a list"`, ...
197    pub fn kind_name(&self) -> &'static str {
198        match self {
199            Value::Null => "null",
200            Value::Bool(_) => "true/false",
201            Value::Int(_) => "an integer",
202            Value::Float(_) => "a number",
203            Value::Str(_) => "a string",
204            Value::List(_) => "a list",
205            Value::Map(_) => "an object",
206            Value::Ref(_) => "a reference",
207            Value::Tag(_) => "a tag",
208            Value::Res(_) => "a resource",
209            Value::Lang(_) => "a translation key",
210            Value::Text(_) => "a text",
211            Value::Image(_) => "an image",
212            Value::Color(_) => "a color",
213            Value::Bytes(_) => "bytes",
214        }
215    }
216
217    pub fn is_null(&self) -> bool {
218        matches!(self, Value::Null)
219    }
220
221    pub fn as_bool(&self) -> Option<bool> {
222        match self {
223            Value::Bool(b) => Some(*b),
224            _ => None,
225        }
226    }
227
228    pub fn as_i64(&self) -> Option<i64> {
229        match self {
230            Value::Int(n) => Some(*n),
231            Value::Float(f) if f.fract() == 0.0 && f.abs() < 9.0e15 => Some(*f as i64),
232            _ => None,
233        }
234    }
235
236    pub fn as_f64(&self) -> Option<f64> {
237        match self {
238            Value::Int(n) => Some(*n as f64),
239            Value::Float(f) => Some(*f),
240            _ => None,
241        }
242    }
243
244    /// The string of a `Str` (and of the string-like typed values: `Text`, `Image`).
245    pub fn as_str(&self) -> Option<&str> {
246        match self {
247            Value::Str(s) | Value::Text(s) | Value::Image(s) => Some(s),
248            _ => None,
249        }
250    }
251
252    pub fn as_list(&self) -> Option<&[Value]> {
253        match self {
254            Value::List(l) => Some(l),
255            _ => None,
256        }
257    }
258
259    pub fn as_map(&self) -> Option<&Map> {
260        match self {
261            Value::Map(m) => Some(m),
262            _ => None,
263        }
264    }
265
266    /// `self[key]` of a map, `None` for anything else.
267    pub fn get(&self, key: &str) -> Option<&Value> {
268        self.as_map()?.get(key)
269    }
270
271    /// The value at `path`.
272    pub fn at(&self, path: &Path) -> Option<&Value> {
273        let mut v = self;
274        for seg in path.segments() {
275            v = match (seg, v) {
276                (Seg::Key(k), Value::Map(m)) => m.get(k)?,
277                (Seg::Index(i), Value::List(l)) => l.get(*i)?,
278                _ => return None,
279            };
280        }
281        Some(v)
282    }
283
284    /// The value `segs` lead to, to change.
285    pub fn at_mut(&mut self, segs: &[Seg]) -> Option<&mut Value> {
286        let mut v = self;
287        for seg in segs {
288            v = match (seg, v) {
289                (Seg::Key(k), Value::Map(m)) => m.get_mut(k)?,
290                (Seg::Index(i), Value::List(l)) => l.get_mut(*i)?,
291                _ => return None,
292            };
293        }
294        Some(v)
295    }
296}
297
298impl From<bool> for Value {
299    fn from(b: bool) -> Self {
300        Value::Bool(b)
301    }
302}
303impl From<i64> for Value {
304    fn from(n: i64) -> Self {
305        Value::Int(n)
306    }
307}
308impl From<f64> for Value {
309    fn from(f: f64) -> Self {
310        Value::Float(f)
311    }
312}
313impl From<&str> for Value {
314    fn from(s: &str) -> Self {
315        Value::Str(s.to_string())
316    }
317}
318impl From<String> for Value {
319    fn from(s: String) -> Self {
320        Value::Str(s)
321    }
322}
323impl From<Vec<Value>> for Value {
324    fn from(l: Vec<Value>) -> Self {
325        Value::List(l)
326    }
327}
328impl From<Map> for Value {
329    fn from(m: Map) -> Self {
330        Value::Map(m)
331    }
332}
333
334/// One step of a [`Path`].
335#[derive(Debug, Clone, PartialEq, Eq, Hash, crate::Schema)]
336#[data(tag = "type")]
337pub enum Seg {
338    Key(String),
339    Index(usize),
340}
341
342/// Where a value is in a document: `entries[2].pages[0].item`.
343#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
344pub struct Path(Vec<Seg>);
345
346impl Path {
347    pub fn root() -> Self {
348        Self(Vec::new())
349    }
350
351    pub fn segments(&self) -> &[Seg] {
352        &self.0
353    }
354
355    pub fn is_root(&self) -> bool {
356        self.0.is_empty()
357    }
358
359    pub fn push(&mut self, seg: Seg) {
360        self.0.push(seg);
361    }
362
363    pub fn pop(&mut self) {
364        self.0.pop();
365    }
366
367    pub fn key(&self, key: &str) -> Path {
368        let mut p = self.clone();
369        p.0.push(Seg::Key(key.to_string()));
370        p
371    }
372
373    /// `rest` under this path.
374    pub fn then(&self, rest: &Path) -> Path {
375        let mut p = self.clone();
376        p.0.extend(rest.0.iter().cloned());
377        p
378    }
379
380    pub fn index(&self, i: usize) -> Path {
381        let mut p = self.clone();
382        p.0.push(Seg::Index(i));
383        p
384    }
385}
386
387/// A path as its steps.
388impl crate::Schema for Path {
389    fn shape() -> crate::Shape {
390        Vec::<Seg>::shape()
391    }
392    fn read(v: &crate::ValueRef<'_>, cx: &mut crate::Reader) -> Option<Self> {
393        Vec::<Seg>::read(v, cx).map(Path)
394    }
395    fn write(&self) -> Value {
396        self.0.write()
397    }
398}
399
400impl fmt::Display for Path {
401    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
402        if self.0.is_empty() {
403            return f.write_str("(root)");
404        }
405        for (i, seg) in self.0.iter().enumerate() {
406            match seg {
407                Seg::Key(k) if is_plain_key(k) => {
408                    if i > 0 {
409                        f.write_str(".")?;
410                    }
411                    f.write_str(k)?;
412                }
413                Seg::Key(k) => write!(f, "[{k:?}]")?,
414                Seg::Index(n) => write!(f, "[{n}]")?,
415            }
416        }
417        Ok(())
418    }
419}
420
421fn is_plain_key(k: &str) -> bool {
422    let mut chars = k.chars();
423    chars
424        .next()
425        .is_some_and(|c| c.is_ascii_alphabetic() || c == '_' || c == '$')
426        && chars.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '$')
427}