/* 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 https://mozilla.org/MPL/2.0/. */ use proc_macro2::Span; use syn::parse::{Parse, ParseStream, Result}; use syn::punctuated::Punctuated; use syn::{braced, token, Attribute, Ident, Path, Token, Type}; #[allow(non_camel_case_types)] mod kw { syn::custom_keyword!(accessor_type); syn::custom_keyword!(gen_accessors); syn::custom_keyword!(gen_types); } pub struct MacroInput { pub type_def: RootTypeDef, pub gen_accessors: Ident, pub accessor_type: Path, } enum MacroArg { GenAccessors(ArgInner), AccessorType(ArgInner), Types(ArgInner), } struct ArgInner { _field_kw: K, _equals: Token![=], value: V, } pub struct Field { pub attributes: Vec, pub name: Ident, _colon: Token![:], pub field_type: FieldType, } pub enum FieldType { Existing(Type), NewTypeDef(NewTypeDef), } pub struct NewTypeDef { _braces: token::Brace, pub fields: Punctuated, } pub struct RootTypeDef { pub type_name: Ident, pub type_def: NewTypeDef, } impl Parse for MacroInput { fn parse(input: ParseStream<'_>) -> Result { let fields: Punctuated = Punctuated::parse_terminated_with(input, MacroArg::parse)?; let mut gen_accessors = None; let mut type_def = None; let mut accessor_type = None; for arg in fields.into_iter() { match arg { MacroArg::GenAccessors(ArgInner { value, .. }) => gen_accessors = Some(value), MacroArg::AccessorType(ArgInner { value, .. }) => accessor_type = Some(value), MacroArg::Types(ArgInner { value, .. }) => type_def = Some(value), } } fn missing_attr(att_name: &str) -> syn::Error { syn::Error::new( Span::call_site(), format!("Expected `{}` attribute", att_name), ) } Ok(MacroInput { type_def: type_def.ok_or_else(|| missing_attr("gen_types"))?, gen_accessors: gen_accessors.ok_or_else(|| missing_attr("gen_accessors"))?, accessor_type: accessor_type.ok_or_else(|| missing_attr("accessor_type"))?, }) } } impl Parse for MacroArg { fn parse(input: ParseStream<'_>) -> Result { let lookahead = input.lookahead1(); if lookahead.peek(kw::gen_types) { Ok(MacroArg::Types(input.parse()?)) } else if lookahead.peek(kw::gen_accessors) { Ok(MacroArg::GenAccessors(input.parse()?)) } else if lookahead.peek(kw::accessor_type) { Ok(MacroArg::AccessorType(input.parse()?)) } else { Err(lookahead.error()) } } } impl Parse for ArgInner { fn parse(input: ParseStream<'_>) -> Result { Ok(ArgInner { _field_kw: input.parse()?, _equals: input.parse()?, value: input.parse()?, }) } } impl Parse for Field { fn parse(input: ParseStream<'_>) -> Result { Ok(Field { attributes: input.call(Attribute::parse_outer)?, name: input.parse()?, _colon: input.parse()?, field_type: input.parse()?, }) } } impl Parse for RootTypeDef { fn parse(input: ParseStream<'_>) -> Result { Ok(RootTypeDef { type_name: input.parse()?, type_def: input.parse()?, }) } } impl Parse for NewTypeDef { fn parse(input: ParseStream<'_>) -> Result { let content; #[allow(clippy::eval_order_dependence)] Ok(NewTypeDef { _braces: braced!(content in input), fields: Punctuated::parse_terminated_with(&content, Field::parse)?, }) } } impl Parse for FieldType { fn parse(input: ParseStream<'_>) -> Result { if input.peek(token::Brace) { Ok(FieldType::NewTypeDef(input.parse()?)) } else { Ok(FieldType::Existing(input.parse()?)) } } }