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