Skip to main content

prebindgen_flat/flat/
spell.rs

1//! Spelling an element back as Rust: the one place the model turns into tokens.
2//!
3//! The other half of **classify off `kind`, spell with `spell()`**. Every helper
4//! here reads an element's retained syntax and emits Rust; none of them decides
5//! what anything *means*. Keeping them out of [`element`](super::element) is
6//! what lets that module describe structure alone — a `Field` is a name, a
7//! position and a type, and whether Rust writes it `id: v` or `v` is answered
8//! here.
9//!
10//! Nothing outside generated Rust reads any of this: a destination language
11//! cannot tell `E::B` from `E::B()`, which is exactly why the delimiters are
12//! spelling rather than a modelled shape.
13
14use proc_macro2::TokenStream;
15use quote::{quote, ToTokens};
16
17use super::element::{Alternative, EnumValue, Field, Struct};
18
19/// Spell a field group: `head`, `head(parts…)` or `head { parts… }`, following
20/// the delimiters the source wrote.
21///
22/// The one place those delimiters are chosen — for match patterns and
23/// constructors alike, in either direction, for a struct and a variant alike.
24/// `B()` carries no payload and still must be written `E::B()` wherever Rust
25/// names it.
26///
27/// `head` is the type's or variant's path, and each part is an already-rendered
28/// [`Field::bind`].
29pub(crate) fn fields(shape: &syn::Fields, head: TokenStream, parts: &[TokenStream]) -> TokenStream {
30    match shape {
31        syn::Fields::Unit => head,
32        syn::Fields::Unnamed(_) => quote!(#head(#(#parts),*)),
33        syn::Fields::Named(_) => quote!(#head { #(#parts),* }),
34    }
35}
36
37/// The three elements that carry their own field delimiters.
38///
39/// `pub(crate)`, so it can bound
40/// [`Emit::shape`](crate::flat::emit::Emit::shape) without becoming a door
41/// itself: an out-of-crate consumer cannot name it, so cannot call through it.
42pub(crate) trait Shaped {
43    fn shape(&self) -> &syn::Fields;
44}
45
46impl Shaped for Alternative {
47    fn shape(&self) -> &syn::Fields {
48        &self.origin.as_syn().fields
49    }
50}
51impl Shaped for EnumValue {
52    fn shape(&self) -> &syn::Fields {
53        &self.origin.as_syn().fields
54    }
55}
56impl Shaped for Struct {
57    fn shape(&self) -> &syn::Fields {
58        &self.origin.as_syn().fields
59    }
60}
61
62impl Field {
63    /// How the field is addressed in a pattern or an initializer: by name when
64    /// it has one, else by position.
65    ///
66    /// Ungated, and the reason is what it reads: [`name`](Self::name) and
67    /// [`index`](Self::index), both model facts. No captured syntax is
68    /// involved, so this is not a door — unlike the `spell` methods above,
69    /// which read the delimiters the source wrote and are
70    /// [`Emit::shape`](crate::flat::emit::Emit::shape)'s to hand out.
71    pub fn member(&self) -> syn::Member {
72        match &self.name {
73            Some(id) => syn::Member::Named(id.clone()),
74            None => syn::Member::Unnamed(syn::Index::from(self.index)),
75        }
76    }
77
78    /// The field bound to `bind`, shaped for whichever address it uses —
79    /// `id: __f0` for a named field, `__f0` for a positional one. The part a
80    /// spelled fields list is built from.
81    pub fn bind(&self, bind: &impl ToTokens) -> TokenStream {
82        match &self.name {
83            Some(id) => quote!(#id: #bind),
84            None => quote!(#bind),
85        }
86    }
87}