let attrs = ContainerAttributes::parse_attrs(&input.attrs, &attr_name)?
.map(Spanning::into_inner)
.unwrap_or_default(); let trait_ident = format_ident!("{trait_name}"); let ident = &input.ident;
let type_params = input
.generics
.params
.iter()
.filter_map(|p| match p {
syn::GenericParam::Type(t) => Some(&t.ident),
syn::GenericParam::Const(..) | syn::GenericParam::Lifetime(..) => None,
})
.collect::<Vec<_>>();
let ctx: ExpansionCtx = (&attrs, &type_params, ident, &trait_ident, &attr_name); let (bounds, body) = match &input.data {
syn::Data::Struct(s) => expand_struct(s, ctx),
syn::Data::Enum(e) => expand_enum(e, ctx),
syn::Data::Union(u) => expand_union(u, ctx),
}?;
let (impl_gens, ty_gens, where_clause) = { let (impl_gens, ty_gens, where_clause) = input.generics.split_for_impl(); letmut where_clause = where_clause
.cloned()
.unwrap_or_else(|| parse_quote! { where });
where_clause.predicates.extend(bounds);
(impl_gens, ty_gens, where_clause)
};
Ok(quote! { #[allow(unreachable_code)] // omit warnings for `!` and other unreachable types #[automatically_derived] impl#impl_gens derive_more::core::fmt::#trait_identfor#ident#ty_gens#where_clause{ fn fmt(
&self, __derive_more_f: &mut derive_more::core::fmt::Formatter<'_>
) -> derive_more::core::fmt::Result { #body
}
}
})
}
/// Type alias for an expansion context: /// - [`ContainerAttributes`]. /// - Type parameters. Slice of [`syn::Ident`]. /// - Struct/enum/union [`syn::Ident`]. /// - Derived trait [`syn::Ident`]. /// - Attribute name [`syn::Ident`]. /// /// [`syn::Ident`]: struct@syn::Ident type ExpansionCtx<'a> = (
&'a ContainerAttributes,
&'a [&'a syn::Ident],
&'a syn::Ident,
&'a syn::Ident,
&'a syn::Ident,
);
/// Expands a [`fmt::Display`]-like derive macro for the provided struct. fn expand_struct(
s: &syn::DataStruct,
(attrs, type_params, ident, trait_ident, _): ExpansionCtx<'_>,
) -> syn::Result<(Vec<syn::WherePredicate>, TokenStream)> { let s = Expansion {
shared_attr: None,
attrs,
fields: &s.fields,
type_params,
trait_ident,
ident,
}; let bounds = s.generate_bounds(); let body = s.generate_body()?;
let vars = s.fields.iter().enumerate().map(|(i, f)| { let var = f.ident.clone().unwrap_or_else(|| format_ident!("_{i}")); let member = f
.ident
.clone()
.map_or_else(|| syn::Member::Unnamed(i.into()), syn::Member::Named);
quote! { let#var = &self.#member;
}
});
let body = quote! { #(#vars )* #body
};
Ok((bounds, body))
}
/// Expands a [`fmt`]-like derive macro for the provided enum. fn expand_enum(
e: &syn::DataEnum,
(container_attrs, type_params, _, trait_ident, attr_name): ExpansionCtx<'_>,
) -> syn::Result<(Vec<syn::WherePredicate>, TokenStream)> { iflet Some(shared_fmt) = &container_attrs.fmt { if shared_fmt
.placeholders_by_arg("_variant")
.any(|p| p.has_modifiers || p.trait_name != "Display")
{ // TODO: This limitation can be lifted, by analyzing the `shared_fmt` deeper and using // `&dyn fmt::TraitName` for transparency instead of just `format_args!()` in the // expansion. return Err(syn::Error::new(
shared_fmt.span(), "shared format `_variant` placeholder cannot contain format specifiers",
));
}
}
let (bounds, match_arms) = e.variants.iter().try_fold(
(Vec::new(), TokenStream::new()),
|(mut bounds, mut arms), variant| { let attrs = ContainerAttributes::parse_attrs(&variant.attrs, attr_name)?
.map(Spanning::into_inner)
.unwrap_or_default(); let ident = &variant.ident;
if attrs.fmt.is_none()
&& variant.fields.is_empty()
&& attr_name != "display"
{ return Err(syn::Error::new(
e.variants.span(),
format!( "implicit formatting of unit enum variant is supported only for `Display` \ macro, use `#[{attr_name}(\"...\")]` to explicitly specify the formatting",
),
));
}
let v = Expansion {
shared_attr: container_attrs.fmt.as_ref(),
attrs: &attrs,
fields: &variant.fields,
type_params,
trait_ident,
ident,
}; let arm_body = v.generate_body()?;
bounds.extend(v.generate_bounds());
/// Helper struct to generate [`Display::fmt()`] implementation body and trait /// bounds for a struct or an enum variant. /// /// [`Display::fmt()`]: fmt::Display::fmt() #[derive(Debug)] struct Expansion<'a> { /// [`FmtAttribute`] shared between all variants of an enum. /// /// [`None`] for a struct.
shared_attr: Option<&'a FmtAttribute>,
impl Expansion<'_> { /// Checks and indicates whether a top-level shared [`FmtAttribute`] is present in this /// [`Expansion`], and whether it has wrapping logic (e.g. uses `_variant` placeholder). fn shared_attr_info(&self) -> (bool, bool) { let shared_attr_contains_variant = self
.shared_attr
.map_or(true, |attr| attr.contains_arg("_variant")); // If `shared_attr` is a transparent call to `_variant`, then we consider it being absent. let has_shared_attr = self.shared_attr.is_some_and(|attr| {
attr.transparent_call().map_or(true, |(_, called_trait)| {
&called_trait != self.trait_ident || !shared_attr_contains_variant
})
});
(
has_shared_attr,
has_shared_attr && shared_attr_contains_variant,
)
}
/// Generates [`Display::fmt()`] implementation for a struct or an enum variant. /// /// # Errors /// /// In case [`FmtAttribute`] is [`None`] and [`syn::Fields`] length is greater than 1. /// /// [`Display::fmt()`]: fmt::Display::fmt() fn generate_body(&self) -> syn::Result<TokenStream> { letmut body = TokenStream::new();
let (has_shared_attr, shared_attr_is_wrapping) = self.shared_attr_info();
let wrap_into_shared_attr = match &self.attrs.fmt {
Some(fmt) => {
body = if shared_attr_is_wrapping { let deref_args = fmt.additional_deref_args(self.fields);
/// Matches the provided [`fmt`] trait `name` to its default formatting placeholder. fn trait_name_to_default_placeholder_literal(name: &syn::Ident) -> &'static str { match () {
_ if name == "Binary" => "{:b}",
_ if name == "Debug" => "{:?}",
_ if name == "Display" => "{}",
_ if name == "LowerExp" => "{:e}",
_ if name == "LowerHex" => "{:x}",
_ if name == "Octal" => "{:o}",
_ if name == "Pointer" => "{:p}",
_ if name == "UpperExp" => "{:E}",
_ if name == "UpperHex" => "{:X}",
_ => unimplemented!(),
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.