xso_proc/
scope.rs

1// Copyright (c) 2024 Jonas Schäfer <jonas@zombofant.net>
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7//! Identifiers used within generated code.
8
9use proc_macro2::Span;
10use syn::*;
11
12use crate::types::ref_ty;
13
14/// Container struct for various identifiers used throughout the parser code.
15///
16/// This struct is passed around from the [`crate::compound::Compound`]
17/// downward to the code generators in order to ensure that everyone is on the
18/// same page about which identifiers are used for what.
19///
20/// The recommended usage is to bind the names which are needed into the local
21/// scope like this:
22///
23/// ```text
24/// # let scope = FromEventsScope::new();
25/// let FromEventsScope {
26///     ref attrs,
27///     ..
28/// } = scope;
29/// ```
30pub(crate) struct FromEventsScope {
31    /// Accesses the `AttrMap` from code in
32    /// [`crate::field::FieldBuilderPart::Init`].
33    pub(crate) attrs: Ident,
34
35    /// Accesses the `String` of a `rxml::Event::Text` event from code in
36    /// [`crate::field::FieldBuilderPart::Text`].
37    pub(crate) text: Ident,
38
39    /// Accesses the builder data during parsing.
40    ///
41    /// This should not be used directly outside [`crate::compound`]. Most of
42    /// the time, using [`Self::access_field`] is the correct way to access
43    /// the builder data.
44    pub(crate) builder_data_ident: Ident,
45
46    /// Accesses the result produced by a nested state's builder type.
47    ///
48    /// See [`crate::field::FieldBuilderPart::Nested`].
49    pub(crate) substate_data: Ident,
50
51    /// Accesses the result produced by a nested state's builder type.
52    ///
53    /// See [`crate::field::FieldBuilderPart::Nested`].
54    pub(crate) substate_result: Ident,
55
56    /// Prefix which should be used for any types which are declared, to
57    /// ensure they don't collide with other names.
58    pub(crate) type_prefix: Ident,
59}
60
61impl FromEventsScope {
62    /// Create a fresh scope with all necessary identifiers.
63    pub(crate) fn new(type_prefix: Ident) -> Self {
64        // Sadly, `Ident::new` is not `const`, so we have to create even the
65        // well-known identifiers from scratch all the time.
66        Self {
67            attrs: Ident::new("attrs", Span::call_site()),
68            text: Ident::new("__xso_proc_macro_text_data", Span::call_site()),
69            builder_data_ident: Ident::new("__xso_proc_macro_builder_data", Span::call_site()),
70            substate_data: Ident::new("__xso_proc_macro_substate_data", Span::call_site()),
71            substate_result: Ident::new("__xso_proc_macro_substate_result", Span::call_site()),
72            type_prefix,
73        }
74    }
75
76    /// Generate an expression which accesses the temporary value for the
77    /// given `member` during parsing.
78    pub(crate) fn access_field(&self, member: &Member) -> Expr {
79        Expr::Field(ExprField {
80            attrs: Vec::new(),
81            base: Box::new(Expr::Path(ExprPath {
82                attrs: Vec::new(),
83                qself: None,
84                path: self.builder_data_ident.clone().into(),
85            })),
86            dot_token: syn::token::Dot {
87                spans: [Span::call_site()],
88            },
89            member: Member::Named(mangle_member(member)),
90        })
91    }
92
93    /// Generate an ident with proper scope and span from the type prefix and
94    /// the given member and actual type name.
95    ///
96    /// Due to being merged with the type prefix of this scope and the given
97    /// member, this type name is guaranteed to be unique for unique values of
98    /// `name`.
99    pub(crate) fn make_member_type_name(&self, member: &Member, name: &str) -> Ident {
100        quote::format_ident!(
101            "{}Member{}{}",
102            self.type_prefix,
103            match member {
104                Member::Named(ref ident) => ident.to_string(),
105                Member::Unnamed(Index { index, .. }) => index.to_string(),
106            },
107            name,
108        )
109    }
110}
111
112/// Container struct for various identifiers used throughout the generator
113/// code.
114///
115/// This struct is passed around from the [`crate::compound::Compound`]
116/// downward to the code generators in order to ensure that everyone is on the
117/// same page about which identifiers are used for what.
118///
119/// See [`FromEventsScope`] for recommendations on the usage.
120pub(crate) struct AsItemsScope {
121    /// Lifetime for data borrowed by the implementation.
122    pub(crate) lifetime: Lifetime,
123
124    /// Prefix which should be used for any types which are declared, to
125    /// ensure they don't collide with other names.
126    pub(crate) type_prefix: Ident,
127}
128
129impl AsItemsScope {
130    /// Create a fresh scope with all necessary identifiers.
131    pub(crate) fn new(lifetime: &Lifetime, type_prefix: Ident) -> Self {
132        Self {
133            lifetime: lifetime.clone(),
134            type_prefix,
135        }
136    }
137
138    /// Create a reference to `ty`, borrowed for the lifetime of the AsXml
139    /// impl.
140    pub(crate) fn borrow(&self, ty: Type) -> Type {
141        ref_ty(ty, self.lifetime.clone())
142    }
143
144    /// Generate an ident with proper scope and span from the type prefix and
145    /// the given member and actual type name.
146    ///
147    /// Due to being merged with the type prefix of this scope and the given
148    /// member, this type name is guaranteed to be unique for unique values of
149    /// `name`.
150    pub(crate) fn make_member_type_name(&self, member: &Member, name: &str) -> Ident {
151        quote::format_ident!(
152            "{}Member{}{}",
153            self.type_prefix,
154            match member {
155                Member::Named(ref ident) => ident.to_string(),
156                Member::Unnamed(Index { index, .. }) => index.to_string(),
157            },
158            name,
159        )
160    }
161}
162
163pub(crate) fn mangle_member(member: &Member) -> Ident {
164    match member {
165        Member::Named(member) => quote::format_ident!("f{}", member),
166        Member::Unnamed(member) => quote::format_ident!("f_u{}", member.index),
167    }
168}