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
70/// A map: entries in file order, keys unique. Keys are shared: a decoded
71/// document allocates each distinct key once, however many maps have it.
72#[derive(Debug, Clone, PartialEq, Default)]
73pub struct Map(Vec<(std::sync::Arc<str>, Value)>);
74
75impl Map {
76    pub fn new() -> Self {
77        Self(Vec::new())
78    }
79
80    pub fn with_capacity(n: usize) -> Self {
81        Self(Vec::with_capacity(n))
82    }
83
84    pub fn get(&self, key: &str) -> Option<&Value> {
85        self.0.iter().find(|(k, _)| &**k == key).map(|(_, v)| v)
86    }
87
88    pub fn get_mut(&mut self, key: &str) -> Option<&mut Value> {
89        self.0.iter_mut().find(|(k, _)| &**k == key).map(|(_, v)| v)
90    }
91
92    /// The value of the `i`-th entry.
93    pub fn value_at(&self, i: usize) -> Option<&Value> {
94        self.0.get(i).map(|(_, v)| v)
95    }
96
97    /// The position of `key` among the entries.
98    pub fn index_of(&self, key: &str) -> Option<usize> {
99        self.0.iter().position(|(k, _)| &**k == key)
100    }
101
102    /// Sets `key`, in place if it's there, else at the end; returns the old value.
103    pub fn insert(&mut self, key: impl Into<String>, value: Value) -> Option<Value> {
104        let key = key.into();
105        match self.get_mut(&key) {
106            Some(slot) => Some(std::mem::replace(slot, value)),
107            None => {
108                self.0.push((key.into(), value));
109                None
110            }
111        }
112    }
113
114    /// Appends without checking for the key — for decoders that already did.
115    pub(crate) fn push_unchecked(&mut self, key: std::sync::Arc<str>, value: Value) {
116        self.0.push((key, value));
117    }
118
119    pub fn remove(&mut self, key: &str) -> Option<Value> {
120        let i = self.index_of(key)?;
121        Some(self.0.remove(i).1)
122    }
123
124    pub fn len(&self) -> usize {
125        self.0.len()
126    }
127
128    pub fn is_empty(&self) -> bool {
129        self.0.is_empty()
130    }
131
132    pub fn iter(&self) -> impl ExactSizeIterator<Item = (&str, &Value)> {
133        self.0.iter().map(|(k, v)| (&**k, v))
134    }
135
136    pub fn keys(&self) -> impl ExactSizeIterator<Item = &str> {
137        self.0.iter().map(|(k, _)| &**k)
138    }
139
140    pub fn retain(&mut self, mut keep: impl FnMut(&str, &Value) -> bool) {
141        self.0.retain(|(k, v)| keep(k, v));
142    }
143}
144
145impl IntoIterator for Map {
146    type Item = (String, Value);
147    type IntoIter = std::iter::Map<std::vec::IntoIter<(std::sync::Arc<str>, Value)>, fn((std::sync::Arc<str>, Value)) -> (String, Value)>;
148    fn into_iter(self) -> Self::IntoIter {
149        self.0.into_iter().map(|(k, v)| (k.to_string(), v))
150    }
151}
152
153impl<K: Into<String>> FromIterator<(K, Value)> for Map {
154    fn from_iter<I: IntoIterator<Item = (K, Value)>>(iter: I) -> Self {
155        let mut map = Map::new();
156        for (k, v) in iter {
157            map.insert(k, v);
158        }
159        map
160    }
161}
162
163impl Value {
164    /// The type's name, for messages: `"a string"`, `"a list"`, ...
165    pub fn kind_name(&self) -> &'static str {
166        match self {
167            Value::Null => "null",
168            Value::Bool(_) => "true/false",
169            Value::Int(_) => "an integer",
170            Value::Float(_) => "a number",
171            Value::Str(_) => "a string",
172            Value::List(_) => "a list",
173            Value::Map(_) => "an object",
174            Value::Ref(_) => "a reference",
175            Value::Tag(_) => "a tag",
176            Value::Res(_) => "a resource",
177            Value::Lang(_) => "a translation key",
178            Value::Text(_) => "a text",
179            Value::Image(_) => "an image",
180            Value::Color(_) => "a color",
181            Value::Bytes(_) => "bytes",
182        }
183    }
184
185    pub fn is_null(&self) -> bool {
186        matches!(self, Value::Null)
187    }
188
189    pub fn as_bool(&self) -> Option<bool> {
190        match self {
191            Value::Bool(b) => Some(*b),
192            _ => None,
193        }
194    }
195
196    pub fn as_i64(&self) -> Option<i64> {
197        match self {
198            Value::Int(n) => Some(*n),
199            Value::Float(f) if f.fract() == 0.0 && f.abs() < 9.0e15 => Some(*f as i64),
200            _ => None,
201        }
202    }
203
204    pub fn as_f64(&self) -> Option<f64> {
205        match self {
206            Value::Int(n) => Some(*n as f64),
207            Value::Float(f) => Some(*f),
208            _ => None,
209        }
210    }
211
212    /// The string of a `Str` (and of the string-like typed values: `Text`, `Image`).
213    pub fn as_str(&self) -> Option<&str> {
214        match self {
215            Value::Str(s) | Value::Text(s) | Value::Image(s) => Some(s),
216            _ => None,
217        }
218    }
219
220    pub fn as_list(&self) -> Option<&[Value]> {
221        match self {
222            Value::List(l) => Some(l),
223            _ => None,
224        }
225    }
226
227    pub fn as_map(&self) -> Option<&Map> {
228        match self {
229            Value::Map(m) => Some(m),
230            _ => None,
231        }
232    }
233
234    /// `self[key]` of a map, `None` for anything else.
235    pub fn get(&self, key: &str) -> Option<&Value> {
236        self.as_map()?.get(key)
237    }
238
239    /// The value at `path`.
240    pub fn at(&self, path: &Path) -> Option<&Value> {
241        let mut v = self;
242        for seg in path.segments() {
243            v = match (seg, v) {
244                (Seg::Key(k), Value::Map(m)) => m.get(k)?,
245                (Seg::Index(i), Value::List(l)) => l.get(*i)?,
246                _ => return None,
247            };
248        }
249        Some(v)
250    }
251}
252
253impl From<bool> for Value {
254    fn from(b: bool) -> Self {
255        Value::Bool(b)
256    }
257}
258impl From<i64> for Value {
259    fn from(n: i64) -> Self {
260        Value::Int(n)
261    }
262}
263impl From<f64> for Value {
264    fn from(f: f64) -> Self {
265        Value::Float(f)
266    }
267}
268impl From<&str> for Value {
269    fn from(s: &str) -> Self {
270        Value::Str(s.to_string())
271    }
272}
273impl From<String> for Value {
274    fn from(s: String) -> Self {
275        Value::Str(s)
276    }
277}
278impl From<Vec<Value>> for Value {
279    fn from(l: Vec<Value>) -> Self {
280        Value::List(l)
281    }
282}
283impl From<Map> for Value {
284    fn from(m: Map) -> Self {
285        Value::Map(m)
286    }
287}
288
289/// One step of a [`Path`].
290#[derive(Debug, Clone, PartialEq, Eq, Hash)]
291pub enum Seg {
292    Key(String),
293    Index(usize),
294}
295
296/// Where a value is in a document: `entries[2].pages[0].item`.
297#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
298pub struct Path(Vec<Seg>);
299
300impl Path {
301    pub fn root() -> Self {
302        Self(Vec::new())
303    }
304
305    pub fn segments(&self) -> &[Seg] {
306        &self.0
307    }
308
309    pub fn is_root(&self) -> bool {
310        self.0.is_empty()
311    }
312
313    pub fn push(&mut self, seg: Seg) {
314        self.0.push(seg);
315    }
316
317    pub fn pop(&mut self) {
318        self.0.pop();
319    }
320
321    pub fn key(&self, key: &str) -> Path {
322        let mut p = self.clone();
323        p.0.push(Seg::Key(key.to_string()));
324        p
325    }
326
327    pub fn index(&self, i: usize) -> Path {
328        let mut p = self.clone();
329        p.0.push(Seg::Index(i));
330        p
331    }
332}
333
334impl fmt::Display for Path {
335    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
336        if self.0.is_empty() {
337            return f.write_str("(root)");
338        }
339        for (i, seg) in self.0.iter().enumerate() {
340            match seg {
341                Seg::Key(k) if is_plain_key(k) => {
342                    if i > 0 {
343                        f.write_str(".")?;
344                    }
345                    f.write_str(k)?;
346                }
347                Seg::Key(k) => write!(f, "[{k:?}]")?,
348                Seg::Index(n) => write!(f, "[{n}]")?,
349            }
350        }
351        Ok(())
352    }
353}
354
355fn is_plain_key(k: &str) -> bool {
356    let mut chars = k.chars();
357    chars
358        .next()
359        .is_some_and(|c| c.is_ascii_alphabetic() || c == '_' || c == '$')
360        && chars.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '$')
361}