//! # Example //! //! `_binded_a`, `_binded_b` and `_binded_0` in below example are //! `BindedField`. //! //! ```rust //! struct S { //! a: u8, //! b: u16, //! } //! let s = S { a: 0, b: 0 }; //! match s { //! S { //! a: _binded_a, //! b: _binded_b, //! } => {} //! } //! enum E { //! V1 { a: u8 }, //! V2(u16), //! V3, //! } //! let e = E::V1 { a: 0 }; //! match e { //! E::V1 { a: _binded_a } => {} //! E::V2(_binded_0) => {} //! E::V3 => {} //! } //! ``` //! //! //! ----- //! //! Adopted from `synstructure`. use pmutil::{prelude::*, *}; use proc_macro2::{Span, TokenStream}; use quote::ToTokens; use syn::{ punctuated::Pair, token::{Mut, Ref}, *, }; use crate::{def_site, is_attr_name, syn_ext::PairExt}; /// Used to bind whole struct or enum. #[derive(Debug, Clone)] pub struct Binder<'a> { ident: &'a Ident, body: &'a Data, attrs: &'a [Attribute], } impl<'a> Binder<'a> { /// /// - `attrs`: Attributes of the type. pub const fn new(ident: &'a Ident, body: &'a Data, attrs: &'a [Attribute]) -> Self { Binder { ident, body, attrs } } pub fn new_from(input: &'a DeriveInput) -> Self { Self::new(&input.ident, &input.data, &input.attrs) } /// pub fn variants(&self) -> Vec> { match *self.body { Data::Enum(DataEnum { ref variants, .. }) => { let enum_name = &self.ident; variants .iter() .map(|v| VariantBinder::new(Some(enum_name), &v.ident, &v.fields, &v.attrs)) .collect() } Data::Struct(DataStruct { ref fields, .. }) => { vec![VariantBinder::new(None, self.ident, fields, self.attrs)] } Data::Union(_) => unimplemented!("Binder for union type"), } } } /// Variant. #[derive(Debug, Clone)] pub struct VariantBinder<'a> { /// None for struct. enum_name: Option<&'a Ident>, /// Name of variant. name: &'a Ident, data: &'a Fields, attrs: &'a [Attribute], } impl<'a> VariantBinder<'a> { pub const fn new( enum_name: Option<&'a Ident>, name: &'a Ident, data: &'a Fields, attrs: &'a [Attribute], ) -> Self { VariantBinder { enum_name, name, data, attrs, } } pub const fn variant_name(&self) -> &Ident { self.name } pub const fn data(&self) -> &Fields { self.data } pub const fn attrs(&self) -> &[Attribute] { self.attrs } /// `EnumName::VariantName` for enum, and `StructName` for struct. pub fn qual_path(&self) -> Path { match self.enum_name { Some(enum_name) => Quote::new(def_site::()) .quote_with(smart_quote!( Vars { EnumName: enum_name, VariantName: self.name, }, { EnumName::VariantName } )) .parse(), None => self.name.clone().into(), } } /// /// - `prefix`: prefix of field binding. pub fn bind( &self, prefix: &str, by_ref: Option, mutability: Option, ) -> (Pat, Vec>) { let path = self.qual_path(); let (pat, bindings) = match *self.data { Fields::Unit => { // EnumName::VariantName let pat = Pat::Path(PatPath { qself: None, path, attrs: Default::default(), }); // Unit struct does not have any field to bind (pat, vec![]) } Fields::Named(FieldsNamed { named: ref fields, brace_token, }) => { let mut bindings = vec![]; let fields = fields .pairs() .map(|e| { let (t, p) = e.into_tuple(); Pair::new(t, p.cloned()) }) .enumerate() .map(|(idx, f)| { f.map_item(|f| { let ident = f .ident .clone() .expect("field of struct-like variants should have name"); let binded_ident = ident.new_ident_with(|s| format!("{}{}", prefix, s)); bindings.push(BindedField { idx, binded_ident: binded_ident.clone(), field: f, }); FieldPat { attrs: f .attrs .iter() .filter(|attr| is_attr_name(attr, "cfg")) .cloned() .collect(), colon_token: f.colon_token, member: Member::Named(ident), pat: Box::new(Pat::Ident(PatIdent { by_ref, mutability, ident: binded_ident, subpat: None, attrs: Default::default(), })), } }) }) .collect(); // EnumName::VariantName { fields } let pat = Pat::Struct(PatStruct { attrs: Default::default(), qself: None, path, brace_token, fields, rest: None, }); (pat, bindings) } Fields::Unnamed(FieldsUnnamed { unnamed: ref fields, paren_token, }) => { // TODO let mut bindings = vec![]; let pats = fields .pairs() .map(|e| { let (t, p) = e.into_tuple(); Pair::new(t, p.cloned()) }) .enumerate() .map(|(idx, f)| { f.map_item(|f| { let binded_ident = def_site::().new_ident(format!("{}{}", prefix, idx)); bindings.push(BindedField { idx, binded_ident: binded_ident.clone(), field: f, }); Pat::Ident(PatIdent { by_ref, mutability, ident: binded_ident, subpat: None, attrs: Default::default(), }) }) }) .collect(); // EnumName::VariantName ( fields ) let pat = Pat::TupleStruct(PatTupleStruct { attrs: Default::default(), qself: None, path, paren_token, elems: pats, }); (pat, bindings) } }; // if we don't need to move fields, we should match on reference to make tuple // work. // let pat = match by_ref { // Some(ref_token) => Pat::Ref(PatRef { // pat: box pat, // and_token: ref_token.0.as_token(), // mutability, // }), // None => pat, // }; (pat, bindings) } } /// Binded field. Note that this struct acts like a binded variable for /// `quote!`. #[derive(Debug, Clone)] pub struct BindedField<'a> { binded_ident: Ident, idx: usize, field: &'a Field, } impl<'a> BindedField<'a> { pub const fn idx(&self) -> usize { self.idx } /// Name of field binding. pub const fn name(&self) -> &Ident { &self.binded_ident } pub const fn field(&self) -> &Field { self.field } } impl<'a> ToTokens for BindedField<'a> { fn to_tokens(&self, t: &mut TokenStream) { self.binded_ident.to_tokens(t) } }