xim_gen/
lib.rs

1//! The code generator used to create the `xim_parser` crate.
2
3#![allow(clippy::uninlined_format_args)]
4
5use crate::format_type::Field;
6use convert_case::{Case, Casing};
7use serde::Deserialize;
8use std::collections::BTreeMap;
9use std::io::{self, Write};
10use std::path::Path;
11
12mod format_type;
13
14#[derive(Deserialize)]
15#[cfg_attr(debug_assertions, derive(Debug, Eq, PartialEq))]
16struct EnumFormat {
17    repr: String,
18    #[serde(default)]
19    bitflag: bool,
20    variants: BTreeMap<String, usize>,
21}
22
23impl EnumFormat {
24    pub fn write(&self, name: &str, out: &mut impl Write) -> io::Result<()> {
25        // reorder variants for variant value
26        let mut variants = self.variants.iter().collect::<Vec<_>>();
27        variants.sort_unstable_by(|l, r| l.1.cmp(r.1));
28
29        if self.bitflag {
30            writeln!(out, "bitflags::bitflags! {{")?;
31            writeln!(out, "#[derive(Clone, Copy, Debug, Eq, PartialEq)]")?;
32            writeln!(out, "pub struct {}: {} {{", name, self.repr)?;
33            for (name, variant) in variants.iter() {
34                writeln!(
35                    out,
36                    "const {} = {};",
37                    name.to_case(Case::UpperSnake),
38                    variant
39                )?;
40            }
41            writeln!(out, "}}")?;
42
43            writeln!(out, "}}")?;
44        } else {
45            writeln!(out, "#[derive(Clone, Copy, Debug, Eq, PartialEq)]")?;
46            writeln!(out, "#[repr({})]", self.repr)?;
47            writeln!(out, "pub enum {} {{", name)?;
48
49            for (name, variant) in variants.iter() {
50                writeln!(out, "{} = {},", name, variant)?;
51            }
52            writeln!(out, "}}")?;
53        }
54
55        writeln!(out, "impl XimRead for {} {{", name)?;
56
57        writeln!(
58            out,
59            "fn read(reader: &mut Reader) -> Result<Self, ReadError> {{ let repr = {}::read(reader)?;", self.repr)?;
60
61        if self.bitflag {
62            writeln!(
63                out,
64                "Self::from_bits(repr).ok_or_else(|| reader.invalid_data(\"{}\", repr))",
65                name
66            )?;
67        } else {
68            writeln!(out, "match repr {{")?;
69            for (name, variants) in variants.iter() {
70                writeln!(out, "{v} => Ok(Self::{n}),", v = variants, n = name)?;
71            }
72
73            writeln!(
74                out,
75                "_ => Err(reader.invalid_data(\"{n}\", repr)),",
76                n = name
77            )?;
78
79            writeln!(out, "}}")?;
80        }
81
82        writeln!(out, "}}")?;
83
84        // impl XimRead
85        writeln!(out, "}}")?;
86
87        writeln!(out, "impl XimWrite for {} {{", name)?;
88
89        writeln!(out, "fn write(&self, writer: &mut Writer) {{")?;
90
91        if self.bitflag {
92            writeln!(out, "self.bits().write(writer);")?;
93        } else {
94            writeln!(out, "(*self as {}).write(writer);", self.repr)?;
95        }
96
97        writeln!(out, "}}")?;
98
99        writeln!(
100            out,
101            "fn size(&self) -> usize {{ core::mem::size_of::<{}>() }}",
102            self.repr
103        )?;
104
105        // impl XimWrite
106        writeln!(out, "}}")?;
107
108        Ok(())
109    }
110}
111
112#[derive(Deserialize)]
113#[cfg_attr(debug_assertions, derive(Debug, Eq, PartialEq))]
114struct RequestFormat {
115    major_opcode: u8,
116    minor_opcode: Option<u8>,
117    body: Vec<Field>,
118}
119
120#[derive(Deserialize)]
121#[cfg_attr(debug_assertions, derive(Debug, Eq, PartialEq))]
122#[serde(transparent)]
123struct StructFormat {
124    body: Vec<Field>,
125}
126
127impl StructFormat {
128    pub fn write(&self, name: &str, out: &mut impl Write) -> io::Result<()> {
129        writeln!(out, "#[derive(Clone, Debug, Eq, PartialEq)]")?;
130        write!(out, "pub struct {}", name)?;
131        writeln!(out, "{{")?;
132
133        for field in self.body.iter() {
134            writeln!(out, "pub {}: {},", field.name, field.ty)?;
135        }
136
137        writeln!(out, "}}")?;
138
139        writeln!(out, "impl XimRead for {} {{", name)?;
140
141        writeln!(
142            out,
143            "fn read(reader: &mut Reader) -> Result<Self, ReadError> {{"
144        )?;
145
146        writeln!(out, "Ok(Self {{")?;
147        for field in self.body.iter() {
148            write!(out, "{}: ", field.name)?;
149            field.ty.read(out)?;
150            write!(out, ",")?;
151        }
152        writeln!(out, "}})")?;
153
154        // fn read
155        writeln!(out, "}}")?;
156        // impl XimRead
157        writeln!(out, "}}")?;
158
159        writeln!(out, "impl XimWrite for {} {{", name)?;
160        writeln!(out, "fn write(&self, writer: &mut Writer) {{")?;
161        for field in self.body.iter() {
162            field.ty.write(&format!("self.{}", field.name), out)?;
163        }
164        // fn write
165        writeln!(out, "}}")?;
166
167        writeln!(out, "fn size(&self) -> usize {{")?;
168        writeln!(out, "let mut content_size = 0;")?;
169
170        for field in self.body.iter() {
171            write!(out, "content_size += ")?;
172            field.ty.size(&format!("self.{}", field.name), out)?;
173            writeln!(out, ";")?;
174        }
175
176        writeln!(out, "content_size")?;
177
178        // fn size
179        writeln!(out, "}}")?;
180
181        // end impl
182        writeln!(out, "}}")?;
183
184        Ok(())
185    }
186}
187
188#[derive(Deserialize)]
189#[cfg_attr(debug_assertions, derive(Debug, Eq, PartialEq))]
190struct XimFormat {
191    #[serde(rename = "Enums")]
192    enums: BTreeMap<String, EnumFormat>,
193    #[serde(rename = "AttributeNames")]
194    attribute_names: BTreeMap<String, String>,
195    #[serde(rename = "Structs")]
196    structs: BTreeMap<String, StructFormat>,
197    #[serde(rename = "Requests")]
198    requests: BTreeMap<String, RequestFormat>,
199}
200
201impl XimFormat {
202    pub fn write(&self, out: &mut impl Write) -> io::Result<()> {
203        for (name, em) in self.enums.iter() {
204            em.write(name, out)?;
205        }
206
207        for (name, st) in self.structs.iter() {
208            st.write(name, out)?;
209        }
210
211        writeln!(
212            out,
213            "#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)]"
214        )?;
215        writeln!(out, "pub enum AttributeName {{")?;
216        for (key, _value) in self.attribute_names.iter() {
217            writeln!(out, "{},", key)?;
218        }
219        writeln!(out, "}}")?;
220
221        writeln!(out, "impl AttributeName {{")?;
222        writeln!(out, "pub fn name(self) -> &'static str {{")?;
223        writeln!(out, "match self {{")?;
224        for (key, value) in self.attribute_names.iter() {
225            writeln!(out, "Self::{} => \"{}\",", key, value)?;
226        }
227        // match
228        writeln!(out, "}}")?;
229        // fn name
230        writeln!(out, "}}")?;
231        // impl AttributeName
232        writeln!(out, "}}")?;
233
234        writeln!(out, "impl XimRead for AttributeName {{")?;
235        writeln!(
236            out,
237            "fn read(reader: &mut Reader) -> Result<Self, ReadError> {{"
238        )?;
239        writeln!(
240            out,
241            "let len = u16::read(reader)?; match reader.consume(len as usize)? {{"
242        )?;
243        for (key, value) in self.attribute_names.iter() {
244            writeln!(out, "b\"{}\" => Ok(Self::{}),", value, key)?;
245        }
246        writeln!(out, "bytes => Err(reader.invalid_data(\"AttributeName\", core::str::from_utf8(bytes).unwrap_or(\"NOT_UTF8\"))),")?;
247        // match
248        writeln!(out, "}}")?;
249        // fn read
250        writeln!(out, "}}")?;
251        // impl XimRead
252        writeln!(out, "}}")?;
253
254        writeln!(out, "impl XimWrite for AttributeName {{")?;
255
256        writeln!(out, "fn write(&self, writer: &mut Writer) {{")?;
257        writeln!(out, "let name = self.name(); (name.len() as u16).write(writer); writer.write(name.as_bytes());")?;
258        // fn write
259        writeln!(out, "}}")?;
260
261        writeln!(out, "fn size(&self) -> usize {{")?;
262        writeln!(out, "self.name().len() + 2")?;
263        // fn size
264        writeln!(out, "}}")?;
265
266        // impl XimWrite
267        writeln!(out, "}}")?;
268
269        writeln!(out, "#[derive(Debug, Clone, Eq, PartialEq)]")?;
270        writeln!(out, "pub enum Request {{")?;
271
272        for (name, req) in self.requests.iter() {
273            writeln!(out, "{} {{", name)?;
274            for field in req.body.iter() {
275                writeln!(out, "{}: {},", field.name, field.ty)?;
276            }
277            writeln!(out, "}},")?;
278        }
279
280        writeln!(out, "}}")?;
281
282        writeln!(out, "impl Request {{")?;
283        writeln!(out, "pub fn name(&self) -> &'static str {{")?;
284        writeln!(out, "match self {{")?;
285        for (name, _req) in self.requests.iter() {
286            writeln!(out, "Request::{} {{ .. }} => \"{}\",", name, name)?;
287        }
288        // match
289        writeln!(out, "}}")?;
290        // fn name
291        writeln!(out, "}}")?;
292        // impl Request
293        writeln!(out, "}}")?;
294
295        writeln!(out, "impl XimRead for Request {{")?;
296
297        writeln!(
298            out,
299            "fn read(reader: &mut Reader) -> Result<Self, ReadError> {{"
300        )?;
301
302        writeln!(
303            out,
304            "let major_opcode = reader.u8()?; let minor_opcode = reader.u8()?; let _length = reader.u16()?;"
305        )?;
306
307        writeln!(out, "match (major_opcode, minor_opcode) {{")?;
308
309        for (name, req) in self.requests.iter() {
310            write!(out, "({}, ", req.major_opcode)?;
311
312            if let Some(minor) = req.minor_opcode {
313                write!(out, "{}", minor)?;
314            } else {
315                write!(out, "_")?;
316            }
317
318            writeln!(out, ") => Ok(Request::{} {{", name)?;
319            for field in req.body.iter() {
320                write!(out, "{}: ", field.name)?;
321                field.ty.read(out)?;
322                write!(out, ",")?;
323            }
324            writeln!(out, "}}),")?;
325        }
326
327        writeln!(out, "_ => Err(reader.invalid_data(\"Opcode\", alloc::format!(\"({{}}, {{}})\", major_opcode, minor_opcode))),")?;
328
329        // match
330        writeln!(out, "}}")?;
331
332        // fn read
333        writeln!(out, "}}")?;
334
335        // impl XimRead
336        writeln!(out, "}}")?;
337
338        writeln!(out, "impl XimWrite for Request {{")?;
339
340        writeln!(out, "fn write(&self, writer: &mut Writer) {{")?;
341
342        writeln!(out, "match self {{")?;
343
344        for (name, req) in self.requests.iter() {
345            writeln!(out, "Request::{} {{", name)?;
346            for field in req.body.iter() {
347                write!(out, "{}, ", field.name)?;
348            }
349            writeln!(out, "}} => {{")?;
350
351            writeln!(out, "{}u8.write(writer);", req.major_opcode)?;
352            writeln!(out, "{}u8.write(writer);", req.minor_opcode.unwrap_or(0))?;
353            writeln!(out, "(((self.size() - 4) / 4) as u16).write(writer);")?;
354
355            for field in req.body.iter() {
356                field.ty.write(&field.name, out)?;
357            }
358
359            writeln!(out, "}}")?;
360        }
361
362        // match
363        writeln!(out, "}}")?;
364
365        // fn write
366        writeln!(out, "}}")?;
367
368        writeln!(out, "fn size(&self) -> usize {{")?;
369        writeln!(out, "let mut content_size = 0;")?;
370
371        writeln!(out, "match self {{")?;
372
373        for (name, req) in self.requests.iter() {
374            writeln!(out, "Request::{} {{", name)?;
375            for field in req.body.iter() {
376                write!(out, "{}, ", field.name)?;
377            }
378            writeln!(out, "}} => {{")?;
379
380            for field in req.body.iter() {
381                write!(out, "content_size += ")?;
382                field.ty.size(&field.name, out)?;
383                writeln!(out, ";")?;
384            }
385
386            writeln!(out, "}}")?;
387        }
388
389        // match
390        writeln!(out, "}}")?;
391        writeln!(out, "content_size + 4")?;
392
393        // fn size
394        writeln!(out, "}}")?;
395
396        // impl XimWrite
397        writeln!(out, "}}")?;
398
399        Ok(())
400    }
401}
402
403pub fn write_format(
404    format_str: &str,
405    out_path: impl AsRef<Path>,
406) -> Result<(), Box<dyn std::error::Error>> {
407    let format: XimFormat = serde_yaml::from_str(format_str)?;
408
409    let mut file = std::io::BufWriter::new(std::fs::File::create(out_path.as_ref())?);
410
411    file.write_all(include_bytes!("../res/snippet.rs"))?;
412    format.write(&mut file)?;
413    file.flush()?;
414
415    let rustfmt = std::process::Command::new("rustfmt")
416        .arg(std::fs::canonicalize(out_path.as_ref())?)
417        .spawn()
418        .expect("call rustfmt")
419        .wait()
420        .unwrap();
421
422    assert!(rustfmt.success());
423
424    Ok(())
425}