Skip to main content

mos_eval/
set_schema.rs

1//! Schema for `#set` directives recognised in MVP 1.5.
2//!
3//! Each [`Target`] (e.g. `page`, `text`, `document`) advertises a fixed
4//! list of `(key, slot type)` pairs. Unknown targets and unknown keys
5//! produce diagnostics in the lowerer (`MOS0011`/`MOS0015`). Slot types drive
6//! the `coerce_value` step and `MOS0020` type-mismatch messages.
7
8#[derive(Copy, Clone, Eq, PartialEq, Debug)]
9pub(crate) enum Target {
10    Page,
11    Text,
12    Document,
13    /// Default styling for `#image(...)` calls; currently just `width`
14    /// and `height` are recognised. The MVP 1.5 emit path doesn't yet
15    /// pick these defaults up on bare images (only explicit per-call
16    /// width/height apply), but accepting them in the schema keeps
17    /// `#set image(width: ...)` from emitting MOS0011 today.
18    Image,
19}
20
21#[derive(Copy, Clone, Eq, PartialEq, Debug)]
22pub(crate) enum SlotType {
23    Str,
24    Float,
25    Length,
26}
27
28impl SlotType {
29    pub(crate) fn expected(self) -> &'static str {
30        match self {
31            Self::Str => "a string",
32            Self::Float => "a number",
33            Self::Length => "a length (e.g. 24mm, 11pt)",
34        }
35    }
36}
37
38struct Slot {
39    key: &'static str,
40    ty: SlotType,
41}
42
43const PAGE_SLOTS: &[Slot] = &[
44    Slot {
45        key: "paper",
46        ty: SlotType::Str,
47    },
48    Slot {
49        key: "margin",
50        ty: SlotType::Length,
51    },
52    Slot {
53        key: "numbering",
54        ty: SlotType::Str,
55    },
56];
57
58const TEXT_SLOTS: &[Slot] = &[
59    Slot {
60        key: "font",
61        ty: SlotType::Str,
62    },
63    Slot {
64        key: "size",
65        ty: SlotType::Length,
66    },
67    Slot {
68        key: "leading",
69        ty: SlotType::Float,
70    },
71];
72
73const DOCUMENT_SLOTS: &[Slot] = &[
74    Slot {
75        key: "title",
76        ty: SlotType::Str,
77    },
78    Slot {
79        key: "author",
80        ty: SlotType::Str,
81    },
82    Slot {
83        key: "language",
84        ty: SlotType::Str,
85    },
86];
87
88const IMAGE_SLOTS: &[Slot] = &[
89    Slot {
90        key: "width",
91        ty: SlotType::Length,
92    },
93    Slot {
94        key: "height",
95        ty: SlotType::Length,
96    },
97];
98
99pub(crate) fn lookup_target(name: &str) -> Option<Target> {
100    match name {
101        "page" => Some(Target::Page),
102        "text" => Some(Target::Text),
103        "document" => Some(Target::Document),
104        "image" => Some(Target::Image),
105        _ => None,
106    }
107}
108
109impl Target {
110    pub(crate) fn name(self) -> &'static str {
111        match self {
112            Self::Page => "page",
113            Self::Text => "text",
114            Self::Document => "document",
115            Self::Image => "image",
116        }
117    }
118
119    fn slots(self) -> &'static [Slot] {
120        match self {
121            Self::Page => PAGE_SLOTS,
122            Self::Text => TEXT_SLOTS,
123            Self::Document => DOCUMENT_SLOTS,
124            Self::Image => IMAGE_SLOTS,
125        }
126    }
127
128    pub(crate) fn slot(self, key: &str) -> Option<SlotType> {
129        self.slots().iter().find(|s| s.key == key).map(|s| s.ty)
130    }
131
132    pub(crate) fn keys(self) -> Vec<&'static str> {
133        self.slots().iter().map(|s| s.key).collect()
134    }
135}