1#[path = "specs_yaml.rs"]
9mod specs_yaml;
10
11use specs_yaml::{
12 AllowedModeYaml, CharacterAttributesYaml, CharacterSpecYaml, CommandKindYaml, CommandSpecYaml,
13 ContentModeYaml, DelimiterSpecYaml, EnvironmentSpecYaml, PackageSpecsYaml,
14};
15
16pub use texform_argspec::ContentMode;
17pub use texform_argspec::{
18 ArgForm, ArgSpec, ArgSpecParseError, DelimiterToken, OwnedArgSpec, ParsedArgSpec, ValueKind,
19 parse_arg_specs,
20};
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum CommandKind {
24 Prefix,
25 Infix,
26 Declarative,
27}
28
29impl CommandKind {
30 pub const fn label(&self) -> &'static str {
31 match self {
32 CommandKind::Prefix => "prefix",
33 CommandKind::Infix => "infix",
34 CommandKind::Declarative => "declarative",
35 }
36 }
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub enum AllowedMode {
41 Math,
42 Text,
43 Both,
44}
45
46impl AllowedMode {
47 pub const fn as_str(self) -> &'static str {
48 match self {
49 AllowedMode::Math => "math",
50 AllowedMode::Text => "text",
51 AllowedMode::Both => "both",
52 }
53 }
54
55 pub const fn union(self, other: Self) -> Self {
56 match (self, other) {
57 (AllowedMode::Both, _) | (_, AllowedMode::Both) => AllowedMode::Both,
58 (AllowedMode::Math, AllowedMode::Math) => AllowedMode::Math,
59 (AllowedMode::Text, AllowedMode::Text) => AllowedMode::Text,
60 (AllowedMode::Math, AllowedMode::Text) | (AllowedMode::Text, AllowedMode::Math) => {
61 AllowedMode::Both
62 }
63 }
64 }
65
66 pub const fn allows(self, mode: ContentMode) -> bool {
67 match self {
68 AllowedMode::Both => true,
69 AllowedMode::Math => matches!(mode, ContentMode::Math),
70 AllowedMode::Text => matches!(mode, ContentMode::Text),
71 }
72 }
73}
74
75impl std::fmt::Display for AllowedMode {
76 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77 f.write_str((*self).as_str())
78 }
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82pub struct BuiltinCommandRecord {
83 pub name: &'static str,
84 pub kind: CommandKind,
85 pub allowed_mode: AllowedMode,
86 pub argspec: ParsedArgSpec,
87 pub tags: &'static [&'static str],
88}
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq)]
91pub struct BuiltinEnvironmentRecord {
92 pub name: &'static str,
93 pub allowed_mode: AllowedMode,
94 pub argspec: ParsedArgSpec,
95 pub body_mode: ContentMode,
96 pub tags: &'static [&'static str],
97}
98
99#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
100pub struct BuiltinCharacterAttributes {
101 pub mathvariant: Option<&'static str>,
102 pub tex_class: Option<&'static str>,
103 pub stretchy: Option<bool>,
104 pub move_sup_sub: Option<bool>,
105 pub large_op: Option<bool>,
106}
107
108#[derive(Debug, Clone, Copy, PartialEq, Eq)]
109pub struct BuiltinCharacterRecord {
110 pub name: &'static str,
111 pub allowed_mode: AllowedMode,
112 pub unicode_value: &'static str,
113 pub attributes: BuiltinCharacterAttributes,
114}
115
116#[derive(Debug, Clone, Copy, PartialEq, Eq)]
117pub struct BuiltinDelimiterRecord {
118 pub name: &'static str,
119 pub is_control_sequence: bool,
120 pub allowed_mode: AllowedMode,
121 pub unicode_value: &'static str,
122 pub attributes: BuiltinCharacterAttributes,
123}
124
125#[derive(Debug, Clone, PartialEq, Eq)]
126pub struct ActiveCommandRecord {
127 pub name: &'static str,
128 pub kind: CommandKind,
129 pub allowed_mode: AllowedMode,
130 pub argspec: ParsedArgSpec,
131 pub tags: &'static [&'static str],
132 pub from_packages: &'static [&'static str],
133}
134
135#[derive(Debug, Clone, PartialEq, Eq)]
136pub struct ActiveEnvironmentRecord {
137 pub name: &'static str,
138 pub allowed_mode: AllowedMode,
139 pub argspec: ParsedArgSpec,
140 pub body_mode: ContentMode,
141 pub tags: &'static [&'static str],
142 pub from_packages: &'static [&'static str],
143}
144
145#[derive(Debug, Default, Clone, PartialEq, Eq)]
146pub struct CharacterAttributes {
147 pub mathvariant: Option<String>,
148 pub tex_class: Option<String>,
149 pub stretchy: Option<bool>,
150 pub move_sup_sub: Option<bool>,
151 pub large_op: Option<bool>,
152}
153
154impl From<BuiltinCharacterAttributes> for CharacterAttributes {
155 fn from(value: BuiltinCharacterAttributes) -> Self {
156 CharacterAttributes {
157 mathvariant: value.mathvariant.map(ToString::to_string),
158 tex_class: value.tex_class.map(ToString::to_string),
159 stretchy: value.stretchy,
160 move_sup_sub: value.move_sup_sub,
161 large_op: value.large_op,
162 }
163 }
164}
165
166#[derive(Debug, Clone, PartialEq, Eq)]
167pub struct ActiveCharacterRecord {
168 pub name: String,
169 pub allowed_mode: AllowedMode,
170 pub unicode_value: String,
171 pub attributes: CharacterAttributes,
172 pub package: String,
173}
174
175#[derive(Debug, Clone, PartialEq, Eq)]
176pub struct ActiveDelimiterRecord {
177 pub name: &'static str,
178 pub is_control_sequence: bool,
179 pub allowed_mode: AllowedMode,
180 pub unicode_value: String,
181 pub attributes: CharacterAttributes,
182 pub package: String,
183}
184
185#[derive(Debug, Clone, PartialEq, Eq)]
186pub struct CommandSpec {
187 pub name: String,
188 pub kind: CommandKind,
189 pub allowed_mode: AllowedMode,
190 pub argspec: OwnedArgSpec,
191 pub tags: Vec<String>,
192}
193
194#[derive(Debug, Clone, PartialEq, Eq)]
195pub struct EnvironmentSpec {
196 pub name: String,
197 pub allowed_mode: AllowedMode,
198 pub argspec: OwnedArgSpec,
199 pub body_mode: ContentMode,
200 pub tags: Vec<String>,
201}
202
203#[derive(Debug, Clone, PartialEq, Eq)]
204pub struct CharacterSpec {
205 pub name: String,
206 pub allowed_mode: AllowedMode,
207 pub unicode_value: String,
208 pub attributes: CharacterAttributes,
209}
210
211#[derive(Debug, Clone, PartialEq, Eq)]
212pub struct DelimiterSpec {
213 pub name: String,
214 pub is_control_sequence: bool,
215 pub allowed_mode: AllowedMode,
216 pub unicode_value: String,
217 pub attributes: CharacterAttributes,
218}
219
220#[derive(Debug, Default, Clone, PartialEq, Eq)]
221pub struct PackageSpecs {
222 pub characters: Vec<CharacterSpec>,
223 pub delimiters: Vec<DelimiterSpec>,
224 pub commands: Vec<CommandSpec>,
225 pub environments: Vec<EnvironmentSpec>,
226}
227
228pub fn load_package_specs_from_str(yaml: &str, context: &str) -> PackageSpecs {
229 let parsed: PackageSpecsYaml = serde_yaml::from_str(yaml)
230 .unwrap_or_else(|e| panic!("failed to parse package specs ({context}): {e}"));
231 parsed.into_specs()
232}
233
234impl PackageSpecsYaml {
235 fn into_specs(self) -> PackageSpecs {
236 PackageSpecs {
237 characters: self.characters.into_iter().map(Into::into).collect(),
238 delimiters: self.delimiters.into_iter().map(Into::into).collect(),
239 commands: self.commands.into_iter().map(Into::into).collect(),
240 environments: self.environments.into_iter().map(Into::into).collect(),
241 }
242 }
243}
244
245impl From<CharacterSpecYaml> for CharacterSpec {
246 fn from(value: CharacterSpecYaml) -> Self {
247 CharacterSpec {
248 name: value.name,
249 allowed_mode: value.allowed_mode.into(),
250 unicode_value: value.unicode_value,
251 attributes: value.attributes.into(),
252 }
253 }
254}
255
256impl From<CharacterAttributesYaml> for CharacterAttributes {
257 fn from(value: CharacterAttributesYaml) -> Self {
258 CharacterAttributes {
259 mathvariant: value.mathvariant,
260 tex_class: value.tex_class,
261 stretchy: value.stretchy,
262 move_sup_sub: value.move_sup_sub,
263 large_op: value.large_op,
264 }
265 }
266}
267
268impl From<DelimiterSpecYaml> for DelimiterSpec {
269 fn from(value: DelimiterSpecYaml) -> Self {
270 DelimiterSpec {
271 name: value.name,
272 is_control_sequence: value.is_control_sequence,
273 allowed_mode: value.allowed_mode.into(),
274 unicode_value: value.unicode_value,
275 attributes: value.attributes.into(),
276 }
277 }
278}
279
280impl From<CommandSpecYaml> for CommandSpec {
281 fn from(value: CommandSpecYaml) -> Self {
282 let context = format!("command {}", value.name);
283 let args =
284 parse_arg_specs(value.argspec.as_str(), context.as_str()).unwrap_or_else(|error| {
285 panic!("{error}");
286 });
287
288 CommandSpec {
289 name: value.name,
290 kind: value.kind.into(),
291 allowed_mode: value.allowed_mode.into(),
292 argspec: OwnedArgSpec {
293 args,
294 source: value.argspec,
295 },
296 tags: value.tags,
297 }
298 }
299}
300
301impl From<CommandKindYaml> for CommandKind {
302 fn from(value: CommandKindYaml) -> Self {
303 match value {
304 CommandKindYaml::Prefix => CommandKind::Prefix,
305 CommandKindYaml::Infix => CommandKind::Infix,
306 CommandKindYaml::Declarative => CommandKind::Declarative,
307 }
308 }
309}
310
311impl From<AllowedModeYaml> for AllowedMode {
312 fn from(value: AllowedModeYaml) -> Self {
313 match value {
314 AllowedModeYaml::Math => AllowedMode::Math,
315 AllowedModeYaml::Text => AllowedMode::Text,
316 AllowedModeYaml::Both => AllowedMode::Both,
317 }
318 }
319}
320
321impl From<EnvironmentSpecYaml> for EnvironmentSpec {
322 fn from(value: EnvironmentSpecYaml) -> Self {
323 let context = format!("environment {}", value.name);
324 let args =
325 parse_arg_specs(value.argspec.as_str(), context.as_str()).unwrap_or_else(|error| {
326 panic!("{error}");
327 });
328
329 EnvironmentSpec {
330 name: value.name,
331 allowed_mode: value.allowed_mode.into(),
332 argspec: OwnedArgSpec {
333 args,
334 source: value.argspec,
335 },
336 body_mode: value.body_mode.into(),
337 tags: value.tags,
338 }
339 }
340}
341
342impl From<ContentModeYaml> for ContentMode {
343 fn from(value: ContentModeYaml) -> Self {
344 match value {
345 ContentModeYaml::Math => ContentMode::Math,
346 ContentModeYaml::Text => ContentMode::Text,
347 }
348 }
349}