Skip to main content

simploxide_bindgen/syntax/
binding.rs

1//! All magic happens here. A module that generates interpreters for SimpleX chat command syntax.
2//!
3//! To add support for another language it's sufficient to implement the
4//! [`SyntaxInterpreter`] trait and then use the following builder pattern:
5//!
6//! ```ignore
7//! use simploxide_bindgen::syntax::binding::BindExt;
8//!
9//! //..
10//!
11//! let interpret_method = syntax_str
12//!     .bind(YourInterpreter::new(api_type))
13//!     // Allows to define, e.g. a function method before the interpreter body
14//!     .prelude(|intrp, out| {
15//!         bufwriteln!(out, :> 4, "def interpret(self):");
16//!         intrp.offset = 8;
17//!     })
18//!     .generate()?;
19//! ```
20
21use crate::{
22    syntax::{EnumSubstitutions, MaybeBool, lex},
23    types::{self, ApiType, EnumType, Field, RecordType, enum_type::EnumVariant},
24};
25use std::fmt::Write as _;
26
27use super::SyntaxElement;
28
29#[macro_export]
30macro_rules! bufwriteln {
31    ($dst:expr, :>$offset:expr, $($args:tt)*) => {
32        write!($dst, "{0:>1$}", " ", $offset).unwrap();
33        writeln!($dst, $($args)*).unwrap();
34    };
35    ($dst:expr, $($args:tt)*) => {
36        writeln!($dst, $($args)*).unwrap();
37    };
38}
39
40/// Represents a type with a syntax that can be interpreted as a SimpleX command. The impl should
41/// return Ok(None) when the syntax string is empty or return full implementation of the following
42/// trait:
43///
44/// ```ignore
45/// trait CommandSyntax {
46///     fn interpret(&self) -> String;
47/// }
48/// ```
49///
50/// The trait definition itself must be generated by the client manually somewhere
51pub trait Interpretable {
52    fn command_syntax_impl(&self) -> Result<Option<String>, String>;
53}
54
55impl Interpretable for ApiType {
56    fn command_syntax_impl(&self) -> Result<Option<String>, String> {
57        match self {
58            ApiType::Record(record_type) => record_type.command_syntax_impl(),
59            ApiType::DiscriminatedUnion(_) => Ok(None),
60            ApiType::Enum(enum_type) => enum_type.command_syntax_impl(),
61        }
62    }
63}
64
65impl Interpretable for EnumType {
66    fn command_syntax_impl(&self) -> Result<Option<String>, String> {
67        self.syntax
68            .bind(EnumInterpreter::with_offset(self, 8))
69            .prelude(|_, code_buffer| {
70                bufwriteln!(code_buffer, "impl CommandSyntax for {} {{", self.name);
71                bufwriteln!(code_buffer, :>4, "const COMMAND_BUF_SIZE: usize = 16;",);
72                bufwriteln!(code_buffer,);
73                bufwriteln!(code_buffer, :>4, "fn append_command_syntax(&self, buf: &mut String) {{",);
74                Ok(())
75            })
76            .postlude(|_, code_buffer| {
77                bufwriteln!(code_buffer, :>4, "}}",);
78                bufwriteln!(code_buffer, "}}",);
79                Ok(())
80            })
81            .generate()
82            .map_err(|e| format!("{e} in syntax {} of the enum type\n{}", self.syntax, self))
83    }
84}
85
86impl Interpretable for RecordType {
87    fn command_syntax_impl(&self) -> Result<Option<String>, String> {
88        self.syntax
89            .bind(RecordInterpreter::with_offset(self, 8))
90            .prelude(|_, out| {
91                bufwriteln!(out, "impl CommandSyntax for {} {{", self.name);
92                if self.syntax.contains("json(") {
93                    bufwriteln!(out, :>4, "const COMMAND_BUF_SIZE: usize = 1024;");
94                } else if self.fields.len() > 2 || self.syntax.contains("[0]>") {
95                    bufwriteln!(out, :>4, "const COMMAND_BUF_SIZE: usize = 256;");
96                } else if self.fields.is_empty() {
97                    bufwriteln!(out, :>4, "const COMMAND_BUF_SIZE: usize = 0;");
98                } else {
99                    bufwriteln!(out, :>4, "const COMMAND_BUF_SIZE: usize = 64;");
100                }
101                bufwriteln!(out,);
102                bufwriteln!(out, :>4, "fn append_command_syntax(&self, buf: &mut String) {{",);
103
104                Ok(())
105            })
106            .postlude(|_, out| {
107                bufwriteln!(out, :>4, "}}");
108                bufwriteln!(out, "}}");
109                Ok(())
110            })
111            .generate()
112            .map_err(|e| format!("{e} in syntax {} of the record type\n{}", self.syntax, self))
113    }
114}
115
116pub type SyntaxInterpreterResult<'a, T, E> = Result<T, SyntaxInterpreterError<'a, E>>;
117
118/// Implement `interpret_` methods and use the provided [`Self::interpret_syntax`] to generate the
119/// whole interpreter body from the syntax.
120///
121/// It's not recommended to use [`Self::interpret_syntax`] directly though, use [`Generator`] that allows to
122/// generate some code around the interpreter body.
123///
124/// By default all methods return [`SyntaxInterpreterError::Unexpected`] error.
125///
126/// The SyntaxElement<'_> parameter is included into each method only to make errors more
127/// informative.
128pub trait SyntaxInterpreter {
129    /// A data that is passed between optional and non-optional contexts. Effectively acts as a
130    /// stack frame that can be used to decide how to render items in [`SyntaxElement::Optional`]
131    /// after processing the optional scope. For example, in Rust this can help to decide which
132    /// syntax to use for the if statement.
133    ///
134    /// Set it to `()` if you don't need it.
135    type ContextData;
136
137    /// A type used in SyntaxInterpreterError::Custom variant.
138    type Error;
139
140    fn interpret_literal<'a>(
141        &mut self,
142        el: SyntaxElement<'a>,
143        _lit: &'a str,
144        _ctx: Option<&mut Self::ContextData>,
145        _out: &mut String,
146    ) -> SyntaxInterpreterResult<'a, (), Self::Error> {
147        Err(SyntaxInterpreterError::Unexpected(el))
148    }
149
150    fn interpret_bool<'a>(
151        &mut self,
152        el: SyntaxElement<'a>,
153        _maybe_bool: MaybeBool,
154        _ctx: Option<&mut Self::ContextData>,
155        _out: &mut String,
156    ) -> SyntaxInterpreterResult<'a, (), Self::Error> {
157        Err(SyntaxInterpreterError::Unexpected(el))
158    }
159
160    fn interpret_enum_substitutions<'a>(
161        &mut self,
162        el: SyntaxElement<'a>,
163        _enum_subs: EnumSubstitutions<'a>,
164        _ctx: Option<&mut Self::ContextData>,
165        _out: &mut String,
166    ) -> SyntaxInterpreterResult<'a, (), Self::Error> {
167        Err(SyntaxInterpreterError::Unexpected(el))
168    }
169
170    fn interpret_trivial_substitution<'a>(
171        &mut self,
172        el: SyntaxElement<'a>,
173        _member_to_sub: &'a str,
174        _ctx: Option<&mut Self::ContextData>,
175        _out: &mut String,
176    ) -> SyntaxInterpreterResult<'a, (), Self::Error> {
177        Err(SyntaxInterpreterError::Unexpected(el))
178    }
179
180    fn interpret_delegate_substitution<'a>(
181        &mut self,
182        el: SyntaxElement<'a>,
183        _member_to_sub: &'a str,
184        _ctx: Option<&mut Self::ContextData>,
185        _out: &mut String,
186    ) -> SyntaxInterpreterResult<'a, (), Self::Error> {
187        Err(SyntaxInterpreterError::Unexpected(el))
188    }
189
190    fn interpret_json_substitution<'a>(
191        &mut self,
192        el: SyntaxElement<'a>,
193        _member_to_sub: &'a str,
194        _ctx: Option<&mut Self::ContextData>,
195        _out: &mut String,
196    ) -> SyntaxInterpreterResult<'a, (), Self::Error> {
197        Err(SyntaxInterpreterError::Unexpected(el))
198    }
199
200    fn interpret_vec_substitution<'a>(
201        &mut self,
202        el: SyntaxElement<'a>,
203        _member_to_sub: &'a str,
204        _delim: &'a str,
205        _ctx: Option<&mut Self::ContextData>,
206        _out: &mut String,
207    ) -> SyntaxInterpreterResult<'a, (), Self::Error> {
208        Err(SyntaxInterpreterError::Unexpected(el))
209    }
210
211    /// Invoked before diving into the optional context. Should return an instance of
212    /// [`Self::ContextData`] that can be mutated by the interpret calls. Then
213    /// [`Self::ContextData`] will be consumed consumed by the matching
214    /// [`Self::interpret_optional_context_exit`].
215    ///
216    /// This [`Self::ContextData`] may be used to decide how to adjust the surrounding code
217    /// depending on the contents of the opitonal context.
218    ///
219    /// If you don't need [`Self::ContextData`] set it to `()`.
220    fn interpret_optional_context_enter<'a>(
221        &mut self,
222        el: SyntaxElement<'a>,
223        _out: &mut String,
224    ) -> SyntaxInterpreterResult<'a, Self::ContextData, Self::Error> {
225        Err(SyntaxInterpreterError::Unexpected(el))
226    }
227
228    /// Invoked right after the optional context was fully processed. Consumes [`Self::ContextData`] tha
229    /// created by the matching `interprert_optional_context_enter`.
230    fn interpret_optional_context_exit<'a>(
231        &mut self,
232        _el: SyntaxElement<'a>,
233        _ctx: Self::ContextData,
234        _out: &mut String,
235    ) -> SyntaxInterpreterResult<'a, (), Self::Error> {
236        Ok(())
237    }
238
239    /// Generates the full body from `syntax`
240    fn interpret_syntax<'a>(
241        &mut self,
242        syntax: &'a str,
243        mut ctx: Option<&mut Self::ContextData>,
244        out: &mut String,
245    ) -> SyntaxInterpreterResult<'a, (), Self::Error> {
246        for tok in lex(syntax) {
247            let el = tok.map_err(SyntaxInterpreterError::Lexer)?;
248
249            match el {
250                SyntaxElement::Literal(lit) => {
251                    self.interpret_literal(el, lit, ctx.as_deref_mut(), out)?
252                }
253                SyntaxElement::EnumSubstitutions(enum_subs) => {
254                    self.interpret_enum_substitutions(el, enum_subs, ctx.as_deref_mut(), out)?
255                }
256                SyntaxElement::MaybeBool(maybe_bool) => {
257                    self.interpret_bool(el, maybe_bool, ctx.as_deref_mut(), out)?
258                }
259                SyntaxElement::TrivialMemberSubstitution { member_name } => {
260                    self.interpret_trivial_substitution(el, member_name, ctx.as_deref_mut(), out)?
261                }
262                SyntaxElement::DelegateMemberSubstitution { member_name } => {
263                    self.interpret_delegate_substitution(el, member_name, ctx.as_deref_mut(), out)?
264                }
265                SyntaxElement::JsonMemberSubstitution { member_name } => {
266                    self.interpret_json_substitution(el, member_name, ctx.as_deref_mut(), out)?
267                }
268                SyntaxElement::VecMemberSubstitution { member_name, delim } => self
269                    .interpret_vec_substitution(el, member_name, delim, ctx.as_deref_mut(), out)?,
270                SyntaxElement::Optional { unparsed } => {
271                    let mut new_ctx = self.interpret_optional_context_enter(el, out)?;
272                    self.interpret_syntax(unparsed, Some(&mut new_ctx), out)?;
273                    self.interpret_optional_context_exit(el, new_ctx, out)?;
274                }
275            }
276        }
277        Ok(())
278    }
279}
280
281pub enum SyntaxInterpreterError<'a, E: 'a> {
282    Unexpected(SyntaxElement<'a>),
283    Lexer(String),
284    Custom(E),
285}
286
287impl<'a, E: 'a + std::fmt::Display> std::fmt::Display for SyntaxInterpreterError<'a, E> {
288    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
289        match self {
290            Self::Unexpected(el) => writeln!(f, "Unexpected syntax element {el:?}"),
291            Self::Lexer(err) => writeln!(f, "Lexer error: {err}"),
292            Self::Custom(err) => writeln!(f, "{err}"),
293        }
294    }
295}
296
297/// It's just a simple closure that provides (interpreter, out_buffer) in its arguments and allows
298/// borrowing the outside context.
299pub type DeferredGeneration<'s, SI> = Box<
300    dyn 's
301        + FnOnce(
302            &mut SI,
303            &mut String,
304        ) -> SyntaxInterpreterResult<'s, (), <SI as SyntaxInterpreter>::Error>,
305>;
306
307/// A helper type that consumes the interpreter and generates the whole code block with it. The
308/// block itself is generated with `prelude` and `postlude` callbacks. E.g. for Python this snippet
309/// can be used to generate the interpret method:
310///
311/// ```ignore
312/// record_type
313///     .syntax
314///     .bind(PythonRecordInterpreter::new(&record_type))
315///     .prelude(|intrp, out| {
316///         bufwriteln!(out, :>4, "def interpret(self):");
317///         intrp.offset = 8;
318///     })
319///     // There is no need to use the .postlude(|intrp, out|) callback because
320///     // in Python functions and methods don't have closing brackets or keywords
321///     .generate()?;
322/// ```
323///
324/// To construct the generator witt a bind method import [`BindExt`] trait.
325pub struct Generator<'s, SI: SyntaxInterpreter> {
326    syntax: &'s str,
327    interpreter: SI,
328    make_prelude: Option<DeferredGeneration<'s, SI>>,
329    make_postlude: Option<DeferredGeneration<'s, SI>>,
330}
331
332impl<'s, SI: SyntaxInterpreter> Generator<'s, SI> {
333    pub fn new(syntax: &'s str, interpreter: SI) -> Self {
334        Self {
335            syntax,
336            interpreter,
337            make_prelude: None,
338            make_postlude: None,
339        }
340    }
341
342    pub fn prelude<F>(mut self, f: F) -> Self
343    where
344        F: 's + FnOnce(&mut SI, &mut String) -> SyntaxInterpreterResult<'s, (), SI::Error>,
345    {
346        self.make_prelude = Some(Box::new(f));
347        self
348    }
349
350    pub fn postlude<F>(mut self, f: F) -> Self
351    where
352        F: 's + FnOnce(&mut SI, &mut String) -> SyntaxInterpreterResult<'s, (), SI::Error>,
353    {
354        self.make_postlude = Some(Box::new(f));
355        self
356    }
357
358    pub fn generate(self) -> SyntaxInterpreterResult<'s, Option<String>, SI::Error> {
359        if self.syntax.is_empty() {
360            return Ok(None);
361        }
362
363        let mut code_buffer = String::with_capacity(4096);
364
365        let Self {
366            syntax,
367            mut interpreter,
368            make_prelude,
369            make_postlude,
370        } = self;
371
372        if let Some(f) = make_prelude {
373            f(&mut interpreter, &mut code_buffer)?;
374        }
375
376        interpreter.interpret_syntax(syntax, None, &mut code_buffer)?;
377
378        if let Some(f) = make_postlude {
379            f(&mut interpreter, &mut code_buffer)?;
380        }
381
382        Ok(Some(code_buffer))
383    }
384}
385
386/// This trait exists to provide syntactic sugar to create the generator builder from str:
387///
388/// ```ignore
389/// "/_command <str(userId)>"
390///     .bind(SomeInterpreter)
391///     .prelude(..)
392///     .postlude(..)
393///     .generate();
394/// ```
395pub trait BindExt<SI: SyntaxInterpreter> {
396    fn bind(&self, interpreter: SI) -> Generator<'_, SI>;
397}
398
399impl<SI: SyntaxInterpreter> BindExt<SI> for str {
400    fn bind(&self, interpreter: SI) -> Generator<'_, SI> {
401        Generator::new(self, interpreter)
402    }
403}
404
405type VariantName<'a> = &'a str;
406type VariantLiteral<'a> = &'a str;
407
408struct EnumInterpreter<'a> {
409    typ: &'a EnumType,
410    offset: usize,
411}
412
413impl<'a> EnumInterpreter<'a> {
414    fn with_offset(typ: &'a EnumType, offset: usize) -> Self {
415        Self { typ, offset }
416    }
417
418    fn interpret_subs(
419        &self,
420        subs: &EnumSubstitutions,
421        from_field: Option<&Field>,
422        code_buffer: &mut String,
423    ) -> Result<(), EnumInterpreterErr> {
424        let (accessor, enum_ref) = match from_field {
425            Some(field) => (format!(".{}", field.rust_name), self.typ.name.as_str()),
426            None => (String::new(), "Self"),
427        };
428
429        let offset = self.offset;
430
431        bufwriteln!(code_buffer, :>offset, "match self{accessor} {{");
432        for (var_name, literal) in self.variant_substitutions(subs)? {
433            bufwriteln!(code_buffer, :>offset + 4, "{enum_ref}::{var_name} => {{");
434            interpret_literal(literal, offset + 8, code_buffer);
435            bufwriteln!(code_buffer, :>offset + 4, "}}");
436        }
437        bufwriteln!(code_buffer, :>offset, "}}");
438        Ok(())
439    }
440
441    fn variant_substitutions<'s>(
442        &self,
443        enum_subs: &'s EnumSubstitutions<'s>,
444    ) -> Result<impl Iterator<Item = (VariantName<'a>, VariantLiteral<'s>)>, EnumInterpreterErr>
445    {
446        let literals_count = enum_subs.iter().count();
447
448        if self.typ.variants.len() != enum_subs.iter().count() {
449            return Err(EnumInterpreterErr {
450                expected: self.typ.variants.len(),
451                got: literals_count,
452            });
453        }
454
455        Ok(self
456            .typ
457            .variants
458            .iter()
459            .map(|var| var.rust_name.as_str())
460            .zip(enum_subs.iter()))
461    }
462}
463
464impl<'this> SyntaxInterpreter for EnumInterpreter<'this> {
465    type Error = EnumInterpreterErr;
466    type ContextData = ();
467
468    fn interpret_literal<'a>(
469        &mut self,
470        _el: SyntaxElement<'a>,
471        lit: &'a str,
472        _ctx: Option<&mut Self::ContextData>,
473        out: &mut String,
474    ) -> SyntaxInterpreterResult<'a, (), Self::Error> {
475        interpret_literal(lit, self.offset, out);
476        Ok(())
477    }
478
479    fn interpret_enum_substitutions<'a>(
480        &mut self,
481        _el: SyntaxElement<'a>,
482        enum_subs: EnumSubstitutions<'a>,
483        _ctx: Option<&mut Self::ContextData>,
484        out: &mut String,
485    ) -> SyntaxInterpreterResult<'a, (), Self::Error> {
486        self.interpret_subs(&enum_subs, None, out)
487            .map_err(SyntaxInterpreterError::Custom)?;
488
489        Ok(())
490    }
491}
492
493pub struct EnumInterpreterErr {
494    expected: usize,
495    got: usize,
496}
497
498impl std::fmt::Display for EnumInterpreterErr {
499    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
500        writeln!(
501            f,
502            "The enum has only {} variants, while the syntax defines {}",
503            self.expected, self.got
504        )
505    }
506}
507
508struct RecordInterpreter<'a> {
509    typ: &'a RecordType,
510    curr_field_ix: usize,
511    offset: usize,
512}
513
514impl<'a> RecordInterpreter<'a> {
515    fn with_offset(typ: &'a RecordType, offset: usize) -> Self {
516        Self {
517            typ,
518            curr_field_ix: 0,
519            offset,
520        }
521    }
522
523    fn current_field<'el>(
524        &self,
525        el: SyntaxElement<'el>,
526    ) -> SyntaxInterpreterResult<'el, &'a Field, String> {
527        self
528            .typ
529            .fields
530            .get(self.curr_field_ix)
531            .ok_or_else(|| {
532                SyntaxInterpreterError::Custom(format!("Expected a field while processing an element {el:?} but found None(current_field_ix={})", self.curr_field_ix))
533            })
534    }
535
536    fn field_by_api_name<'el>(
537        &mut self,
538        api_name: &str,
539        el: SyntaxElement<'el>,
540    ) -> SyntaxInterpreterResult<'el, &'a Field, String> {
541        loop {
542            let field = self.typ.fields.get(self.curr_field_ix).ok_or_else(|| {
543                SyntaxInterpreterError::Custom(format!("Failed to find a struct field with the name: {api_name:?} while trying to match element {el:?}"))
544            })?;
545
546            if field.api_name == api_name {
547                return Ok(field);
548            }
549
550            self.curr_field_ix += 1;
551        }
552    }
553}
554
555impl<'this> SyntaxInterpreter for RecordInterpreter<'this> {
556    type Error = String;
557    type ContextData = RecordContextData<'this>;
558
559    fn interpret_literal<'a>(
560        &mut self,
561        _el: SyntaxElement<'a>,
562        lit: &'a str,
563        ctx: Option<&mut Self::ContextData>,
564        out: &mut String,
565    ) -> SyntaxInterpreterResult<'a, (), Self::Error> {
566        match ctx {
567            Some(frame) => interpret_literal(lit, self.offset, &mut frame.buffer),
568            None => interpret_literal(lit, self.offset, out),
569        }
570
571        Ok(())
572    }
573
574    fn interpret_bool<'a>(
575        &mut self,
576        el: SyntaxElement<'a>,
577        maybe_bool: MaybeBool,
578        ctx: Option<&mut Self::ContextData>,
579        out: &mut String,
580    ) -> SyntaxInterpreterResult<'a, (), Self::Error> {
581        match maybe_bool {
582            MaybeBool::On | MaybeBool::Off => {
583                if let Some(frame) = ctx {
584                    let maybe_bool_field = self.current_field(el)?;
585
586                    if !maybe_bool_field.is_bool() {
587                        return Err(SyntaxInterpreterError::Custom(format!(
588                            "Expected a regular bool field but got {maybe_bool:?} while processing an element {el:?} in the optional span"
589                        )));
590                    }
591                    if maybe_bool == MaybeBool::On {
592                        interpret_literal("on", self.offset, &mut frame.buffer);
593                        frame.cond_kind = CondKind::Bool(maybe_bool_field);
594                    } else {
595                        interpret_literal("off", self.offset, &mut frame.buffer);
596                        frame.cond_kind = CondKind::NotBool(maybe_bool_field);
597                    }
598                } else {
599                    return Err(SyntaxInterpreterError::Custom(
600                        "Unexpexted non-optional '{val:?}' literal that doesn't provide a choice. Expected (on|off)"
601                            .to_owned(),
602                    ));
603                }
604            }
605            MaybeBool::Either => {
606                let bool_field = self.current_field(el)?;
607
608                let (self_str, deref, dest) = if let Some(frame) = ctx {
609                    if !bool_field.is_optional() {
610                        return Err(SyntaxInterpreterError::Custom(format!(
611                            "Expected an optional bool field but got {bool_field:?} while processing an element {el:?}"
612                        )));
613                    }
614
615                    frame.cond_kind = CondKind::Opt(bool_field);
616                    ("", "*", &mut frame.buffer)
617                } else {
618                    if !bool_field.is_bool() {
619                        return Err(SyntaxInterpreterError::Custom(format!(
620                            "The current field must be a bool, but instead it's a {bool_field:?} for element {el:?}",
621                        )));
622                    }
623
624                    ("self.", "", out)
625                };
626
627                bufwriteln!(dest, :> self.offset, "if {deref}{self_str}{} {{", bool_field.rust_name);
628                interpret_literal("on", self.offset + 4, dest);
629                bufwriteln!(dest, :> self.offset, "}} else {{");
630                interpret_literal("off", self.offset + 4, dest);
631                bufwriteln!(dest, :> self.offset, "}}");
632            }
633        }
634
635        self.curr_field_ix += 1;
636        Ok(())
637    }
638
639    fn interpret_enum_substitutions<'a>(
640        &mut self,
641        el: SyntaxElement<'a>,
642        enum_subs: EnumSubstitutions<'a>,
643        ctx: Option<&mut Self::ContextData>,
644        out: &mut String,
645    ) -> SyntaxInterpreterResult<'a, (), Self::Error> {
646        if ctx.is_some() {
647            // Add on demand
648            return Err(SyntaxInterpreterError::Custom(format!(
649                "Enum substitutions are unsupported in optional contexts, but got {el:?}",
650            )));
651        }
652
653        let field = self.current_field(el)?;
654
655        if !field.is_compound() {
656            return Err(SyntaxInterpreterError::Custom(format!(
657                "Expected a enum field but got a {:?} field while processing {el:?}",
658                field.typ,
659            )));
660        }
661
662        let ad_hoc_enum = EnumType::new(
663            field.typ.clone(),
664            enum_subs.iter().map(EnumVariant::from_api_name).collect(),
665        );
666
667        let interpreter = EnumInterpreter::with_offset(&ad_hoc_enum, self.offset);
668        interpreter
669            .interpret_subs(&enum_subs, Some(field), out)
670            .map_err(|e| SyntaxInterpreterError::Custom(e.to_string()))?;
671
672        self.curr_field_ix += 1;
673        Ok(())
674    }
675
676    fn interpret_trivial_substitution<'a>(
677        &mut self,
678        el: SyntaxElement<'a>,
679        member_to_sub: &'a str,
680        ctx: Option<&mut Self::ContextData>,
681        out: &mut String,
682    ) -> SyntaxInterpreterResult<'a, (), Self::Error> {
683        let field = self.field_by_api_name(member_to_sub, el)?;
684
685        let (self_str, unwrap, dest) = match ctx {
686            Some(frame) => {
687                frame.cond_kind = CondKind::Opt(field);
688                ("", "", &mut frame.buffer)
689            }
690            None => ("self.", maybe_unwrap(field), out),
691        };
692
693        bufwriteln!(dest, :> self.offset, "write!(buf, \"{{}}\", {self_str}{}{unwrap}).unwrap();", field.rust_name);
694
695        self.curr_field_ix += 1;
696        Ok(())
697    }
698
699    fn interpret_delegate_substitution<'a>(
700        &mut self,
701        el: SyntaxElement<'a>,
702        member_to_sub: &'a str,
703        ctx: Option<&mut Self::ContextData>,
704        out: &mut String,
705    ) -> SyntaxInterpreterResult<'a, (), Self::Error> {
706        let field = self.field_by_api_name(member_to_sub, el)?;
707
708        let (self_str, dest) = match ctx {
709            Some(frame) => {
710                frame.cond_kind = CondKind::Opt(field);
711                ("", &mut frame.buffer)
712            }
713            None => ("self.", out),
714        };
715
716        bufwriteln!(dest, :> self.offset, "{self_str}{}.append_command_syntax(buf);", field.rust_name);
717
718        self.curr_field_ix += 1;
719        Ok(())
720    }
721
722    fn interpret_json_substitution<'a>(
723        &mut self,
724        el: SyntaxElement<'a>,
725        member_to_sub: &'a str,
726        ctx: Option<&mut Self::ContextData>,
727        out: &mut String,
728    ) -> SyntaxInterpreterResult<'a, (), Self::Error> {
729        let field = self.field_by_api_name(member_to_sub, el)?;
730
731        let (self_str, dest) = match ctx {
732            Some(frame) => {
733                frame.cond_kind = CondKind::Opt(field);
734                ("", &mut frame.buffer)
735            }
736            None => ("self.", out),
737        };
738
739        bufwriteln!(
740            dest,
741            :> self.offset, "// SAFETY: serde_json guarantees to produce valid UTF-8 sequences",
742        );
743        bufwriteln!(
744            dest,
745            :> self.offset, "unsafe {{ serde_json::to_writer(buf.as_mut_vec(), &{self_str}{}).unwrap(); }}",
746            field.rust_name,
747        );
748
749        self.curr_field_ix += 1;
750        Ok(())
751    }
752
753    fn interpret_vec_substitution<'a>(
754        &mut self,
755        el: SyntaxElement<'a>,
756        member_to_sub: &'a str,
757        delim: &'a str,
758        ctx: Option<&mut Self::ContextData>,
759        out: &mut String,
760    ) -> SyntaxInterpreterResult<'a, (), Self::Error> {
761        let field = self.field_by_api_name(member_to_sub, el)?;
762
763        let (self_str, unwrap, dest) = match ctx {
764            Some(frame) => {
765                frame.cond_kind = CondKind::Opt(field);
766                ("", "", &mut frame.buffer)
767            }
768            None => ("self.", maybe_unwrap(field), out),
769        };
770
771        bufwriteln!(dest, :> self.offset, "let mut iter = {self_str}{}{unwrap}.iter();", field.rust_name);
772        bufwriteln!(dest, :> self.offset, "if let Some(el) = iter.next() {{");
773        bufwriteln!(dest, :> self.offset + 4, "write!(buf, \"{{el}}\").unwrap();");
774        bufwriteln!(dest, :> self.offset, "}}");
775        bufwriteln!(dest, :> self.offset, "for el in iter {{");
776        interpret_literal(delim, self.offset + 4, dest);
777        bufwriteln!(dest, :> self.offset + 4, "write!(buf, \"{{el}}\").unwrap();");
778        bufwriteln!(dest, :> self.offset, "}}");
779
780        self.curr_field_ix += 1;
781        Ok(())
782    }
783
784    fn interpret_optional_context_enter<'a>(
785        &mut self,
786        _el: SyntaxElement<'a>,
787        _out: &mut String,
788    ) -> SyntaxInterpreterResult<'a, Self::ContextData, Self::Error> {
789        self.offset += 4;
790        Ok(RecordContextData {
791            buffer: String::with_capacity(256),
792            cond_kind: CondKind::None,
793        })
794    }
795
796    fn interpret_optional_context_exit<'a>(
797        &mut self,
798        el: SyntaxElement<'a>,
799        ctx: Self::ContextData,
800        out: &mut String,
801    ) -> SyntaxInterpreterResult<'a, (), Self::Error> {
802        self.offset -= 4;
803
804        match ctx.cond_kind {
805            CondKind::Bool(field) => {
806                bufwriteln!(out, :> self.offset, "if self.{} {{", field.rust_name);
807            }
808            CondKind::NotBool(field) => {
809                bufwriteln!(out, :> self.offset, "if !self.{} {{", field.rust_name);
810            }
811            CondKind::Opt(field) => {
812                bufwriteln!(out, :> self.offset, "if let Some({0}) = &self.{0} {{", field.rust_name);
813            }
814            CondKind::None => {
815                return Err(SyntaxInterpreterError::Custom(format!(
816                    "Failed to deduce a field type in optional context while processing {el:?}"
817                )));
818            }
819        }
820
821        out.push_str(&ctx.buffer);
822
823        bufwriteln!(out, :> self.offset, "}}");
824        Ok(())
825    }
826}
827
828pub struct RecordContextData<'a> {
829    buffer: String,
830    cond_kind: CondKind<'a>,
831}
832
833enum CondKind<'a> {
834    Bool(&'a Field),
835    NotBool(&'a Field),
836    Opt(&'a Field),
837    None,
838}
839
840fn interpret_literal(literal: &str, offset: usize, code_buffer: &mut String) {
841    if literal.len() == 1 {
842        bufwriteln!(code_buffer, :>offset, "buf.push('{literal}');");
843    } else {
844        bufwriteln!(code_buffer, :>offset, "buf.push_str(\"{literal}\");");
845    }
846}
847
848fn maybe_unwrap(field: &Field) -> &'static str {
849    if let Some(inner) = field.inner_type() {
850        if field.is_optional() && types::is_string_type(inner) {
851            ".as_deref().unwrap_or_default()"
852        } else if field.is_vec() {
853            ""
854        } else {
855            ".unwrap_or_default()"
856        }
857    } else {
858        ""
859    }
860}
861
862#[cfg(test)]
863mod tests {
864    use super::*;
865    use expect_test::expect;
866
867    #[test]
868    fn enum_interpreter() {
869        let mut test_enum = EnumType::new(
870            "Greeting".to_owned(),
871            vec!["\"hi\"".parse().unwrap(), "\"bye\"".parse().unwrap()],
872        );
873
874        test_enum.syntax = "Hello,|Goodbye,| World!".to_owned();
875
876        let interpreter_impl = test_enum.command_syntax_impl().unwrap().unwrap();
877
878        expect![[r#"
879            impl CommandSyntax for Greeting {
880                const COMMAND_BUF_SIZE: usize = 16;
881
882                fn append_command_syntax(&self, buf: &mut String) {
883                    match self {
884                        Self::Hi => {
885                            buf.push_str("Hello,");
886                        }
887                        Self::Bye => {
888                            buf.push_str("Goodbye,");
889                        }
890                    }
891                    buf.push(' ');
892                    buf.push_str("World!");
893                }
894            }
895        "#]]
896        .assert_eq(&interpreter_impl);
897    }
898
899    trait CommandSyntax {
900        const COMMAND_BUF_SIZE: usize;
901
902        fn to_command_string(&self) -> String {
903            let mut buf = String::with_capacity(Self::COMMAND_BUF_SIZE);
904            self.append_command_syntax(&mut buf);
905            buf
906        }
907
908        fn append_command_syntax(&self, buf: &mut String);
909    }
910
911    enum Greeting {
912        Hi,
913        Bye,
914    }
915
916    impl CommandSyntax for Greeting {
917        const COMMAND_BUF_SIZE: usize = 16;
918        fn append_command_syntax(&self, buf: &mut String) {
919            match self {
920                Self::Hi => {
921                    buf.push_str("Hello,");
922                }
923                Self::Bye => {
924                    buf.push_str("Goodbye,");
925                }
926            }
927            buf.push(' ');
928            buf.push_str("World!");
929        }
930    }
931
932    #[test]
933    fn real_greeting() {
934        assert_eq!(Greeting::Hi.to_command_string(), "Hello, World!");
935        assert_eq!(Greeting::Bye.to_command_string(), "Goodbye, World!");
936    }
937}