Skip to main content

yog_data/schema/
shape.rs

1//! Shapes: what a format looks like.
2
3use super::{Map, Value};
4use yog_ids::shape_kind as kind;
5
6// ── Shapes ──────────────────────────────────────────────────────────────────
7
8/// A shape, built on demand — so shapes of recursive types are fine.
9pub type ShapeFn = fn() -> Shape;
10
11/// What a format looks like: drives Yog-IDLE's property grids and pickers,
12/// completion, hover docs and the docs' field tables.
13#[derive(Debug, Clone)]
14pub enum Shape {
15    /// Any value.
16    Any,
17    Bool,
18    Int {
19        min: i64,
20        max: i64,
21    },
22    /// A number with a fraction, `bits` wide (32 or 64): how a frame packs it.
23    Float {
24        bits: u32,
25    },
26    /// Plain text.
27    Str,
28    /// A name that repeats ([`crate::Atom`]): text everywhere, its number in a frame.
29    Atom,
30    /// Yog text, a `@Set.key` resource, or `@kind:id` (that content's name).
31    Text,
32    /// An image source or a `@Set.key` image resource.
33    Image,
34    /// `#RRGGBB`/`#AARRGGBB` or a `@Set.key` color resource.
35    Color,
36    /// Bytes as they are.
37    Bytes,
38    /// A mod resource, `@Set.key`.
39    Res,
40    /// Content of a kind (`""`: any kind, written `@kind:id`).
41    Ref(&'static [&'static str]),
42    /// A tag of content of a kind.
43    Tag(&'static [&'static str]),
44    /// Content of a kind or a tag of it.
45    RefOrTag(&'static [&'static str]),
46    /// Content of a kind (`@item:…`) or an image source.
47    RefOrImage(&'static [&'static str]),
48    List(ShapeFn),
49    /// An object of any keys, every value this shape.
50    Map(ShapeFn),
51    /// A value or null.
52    Optional(ShapeFn),
53    Struct(StructShape),
54    Enum(EnumShape),
55}
56
57#[derive(Debug, Clone)]
58pub struct StructShape {
59    pub name: &'static str,
60    pub doc: &'static str,
61    pub fields: Vec<Field>,
62}
63
64#[derive(Debug, Clone)]
65pub struct Field {
66    pub name: &'static str,
67    pub doc: &'static str,
68    pub shape: ShapeFn,
69    /// Must be written (no default).
70    pub required: bool,
71    /// The numbers it holds must be in `min..=max` (`#[data(range = "1..=99")]`).
72    pub range: Option<(f64, f64)>,
73}
74
75#[derive(Debug, Clone)]
76pub struct EnumShape {
77    pub name: &'static str,
78    pub doc: &'static str,
79    /// The key naming the variant (`"type"`), `None` for an enum of names.
80    pub tag: Option<&'static str>,
81    pub variants: Vec<Variant>,
82}
83
84#[derive(Debug, Clone)]
85pub struct Variant {
86    pub name: &'static str,
87    pub doc: &'static str,
88    /// The variant's fields (none for a name).
89    pub fields: Vec<Field>,
90}
91
92mod key;
93
94/// A description's map of `kind`.
95fn of_kind(k: &str) -> Map {
96    let mut m = Map::new();
97    m.insert(key::KIND, Value::from(k));
98    m
99}
100
101/// `doc` under its key, unless it's empty.
102fn put_doc(m: &mut Map, doc: &str) {
103    if !doc.is_empty() {
104        m.insert(key::DOC, Value::from(doc));
105    }
106}
107
108/// A reference to a struct or enum in `defs`.
109fn def_ref(name: &str) -> Map {
110    let mut m = of_kind(kind::DEF);
111    m.insert(key::NAME, Value::from(name));
112    m
113}
114
115fn describe_fields(fs: &[Field], defs: &mut Map) -> Value {
116    Value::List(
117        fs.iter()
118            .map(|f| {
119                let mut m = Map::new();
120                m.insert(key::NAME, Value::from(f.name));
121                put_doc(&mut m, f.doc);
122                m.insert(key::SHAPE, (f.shape)().describe_into(defs));
123                m.insert(key::REQUIRED, Value::Bool(f.required));
124                if let Some((lo, hi)) = f.range {
125                    m.insert(
126                        key::RANGE,
127                        Value::List(vec![Value::Float(lo), Value::Float(hi)]),
128                    );
129                }
130                Value::Map(m)
131            })
132            .collect(),
133    )
134}
135
136impl StructShape {
137    /// Written into `defs` (once: a recursive type finds itself there).
138    fn define(&self, defs: &mut Map) {
139        if defs.get(self.name).is_some() {
140            return;
141        }
142        // Placeholder first: a recursive type finds itself defined.
143        defs.insert(self.name, Value::Null);
144        let mut d = of_kind(kind::STRUCT);
145        put_doc(&mut d, self.doc);
146        d.insert(key::FIELDS, describe_fields(&self.fields, defs));
147        defs.insert(self.name, Value::Map(d));
148    }
149}
150
151impl EnumShape {
152    /// Written into `defs` (once).
153    fn define(&self, defs: &mut Map) {
154        if defs.get(self.name).is_some() {
155            return;
156        }
157        defs.insert(self.name, Value::Null);
158        let mut d = of_kind(kind::ENUM);
159        put_doc(&mut d, self.doc);
160        if let Some(tag) = self.tag {
161            d.insert(key::TAG, Value::from(tag));
162        }
163        let variants = self
164            .variants
165            .iter()
166            .map(|v| {
167                let mut m = Map::new();
168                m.insert(key::NAME, Value::from(v.name));
169                put_doc(&mut m, v.doc);
170                if self.tag.is_some() {
171                    m.insert(key::FIELDS, describe_fields(&v.fields, defs));
172                }
173                Value::Map(m)
174            })
175            .collect();
176        d.insert(key::VARIANTS, Value::List(variants));
177        defs.insert(self.name, Value::Map(d));
178    }
179}
180
181impl Shape {
182    /// The shape as Yog Data, for tools in other languages (Yog-IDLE).
183    /// Structs and enums are written once in `defs` and referred to by name.
184    pub fn describe(&self) -> Value {
185        let mut defs = Map::new();
186        let root = self.describe_into(&mut defs);
187        let mut m = Map::new();
188        m.insert(key::ROOT, root);
189        m.insert(key::DEFS, Value::Map(defs));
190        Value::Map(m)
191    }
192
193    /// The word a plain shape (one with nothing more to say) is described by.
194    fn plain_kind(&self) -> Option<&'static str> {
195        Some(match self {
196            Shape::Any => kind::ANY,
197            Shape::Bool => kind::BOOL,
198            Shape::Str => kind::STRING,
199            Shape::Atom => kind::ATOM,
200            Shape::Text => kind::TEXT,
201            Shape::Image => kind::IMAGE,
202            Shape::Color => kind::COLOR,
203            Shape::Bytes => kind::BYTES,
204            Shape::Res => kind::RES,
205            _ => return None,
206        })
207    }
208
209    fn describe_into(&self, defs: &mut Map) -> Value {
210        if let Some(k) = self.plain_kind() {
211            return Value::Map(of_kind(k));
212        }
213        let m = match self {
214            Shape::Int { min, max } => {
215                let mut m = of_kind(kind::INT);
216                m.insert(key::MIN, Value::Int(*min));
217                m.insert(key::MAX, Value::Int(*max));
218                m
219            }
220            Shape::Float { bits } => {
221                let mut m = of_kind(kind::FLOAT);
222                m.insert(key::BITS, Value::Int(i64::from(*bits)));
223                m
224            }
225            Shape::Ref(k) | Shape::Tag(k) | Shape::RefOrTag(k) | Shape::RefOrImage(k) => {
226                let mut m = of_kind(match self {
227                    Shape::Ref(_) => kind::REF,
228                    Shape::Tag(_) => kind::TAG,
229                    Shape::RefOrTag(_) => kind::REF_OR_TAG,
230                    _ => kind::REF_OR_IMAGE,
231                });
232                m.insert(
233                    key::OF,
234                    Value::List(k.iter().map(|k| Value::from(*k)).collect()),
235                );
236                m
237            }
238            Shape::List(s) | Shape::Map(s) | Shape::Optional(s) => {
239                let mut m = of_kind(match self {
240                    Shape::List(_) => kind::LIST,
241                    Shape::Map(_) => kind::MAP,
242                    _ => kind::OPTIONAL,
243                });
244                m.insert(key::OF, s().describe_into(defs));
245                m
246            }
247            Shape::Struct(s) => {
248                s.define(defs);
249                def_ref(s.name)
250            }
251            Shape::Enum(e) => {
252                e.define(defs);
253                def_ref(e.name)
254            }
255            _ => Map::new(),
256        };
257        Value::Map(m)
258    }
259}