deluxe-core-0.5.0/.cargo_vcs_info.json0000644000000001420000000000100132470ustar { "git": { "sha1": "550c8ea2831f7104bf3c457db1ea9513c2fa1c31" }, "path_in_vcs": "core" }deluxe-core-0.5.0/COPYING000064400000000000000000000020661046102023000131010ustar 00000000000000MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. deluxe-core-0.5.0/Cargo.toml0000644000000023350000000000100112530ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "deluxe-core" version = "0.5.0" description = "Core traits and helpers for Deluxe procedural macro attribute parser" homepage = "https://github.com/jf2048/deluxe" documentation = "https://docs.rs/deluxe-core" license = "MIT" repository = "https://github.com/jf2048/deluxe.git" [lib] path = "lib.rs" [dependencies.arrayvec] version = "0.7.2" [dependencies.proc-macro2] version = "1.0.38" [dependencies.quote] version = "1.0.25" [dependencies.strsim] version = "0.10.0" [dependencies.syn] version = "2.0.10" features = [ "clone-impls", "derive", "extra-traits", "parsing", "printing", ] default-features = false [features] default = [ "full", "proc-macro", ] full = ["syn/full"] proc-macro = ["syn/proc-macro"] deluxe-core-0.5.0/Cargo.toml.orig000064400000000000000000000012201046102023000147240ustar 00000000000000[package] name = "deluxe-core" version = "0.5.0" edition = "2021" description = "Core traits and helpers for Deluxe procedural macro attribute parser" license = "MIT" documentation = "https://docs.rs/deluxe-core" homepage = "https://github.com/jf2048/deluxe" repository = "https://github.com/jf2048/deluxe.git" [lib] path = "lib.rs" [features] default = ["full", "proc-macro"] full = ["syn/full"] proc-macro = ["syn/proc-macro"] [dependencies] arrayvec = "0.7.2" proc-macro2 = "1.0.38" quote = "1.0.25" strsim = "0.10.0" syn = { version = "2.0.10", features = ["clone-impls", "derive", "extra-traits", "parsing", "printing"], default-features = false } deluxe-core-0.5.0/lib.rs000064400000000000000000000024441046102023000131620ustar 00000000000000//! # Deluxe Core //! //! Core functions and traits shared between [`deluxe`](https://docs.rs/deluxe) and //! [`deluxe_macros`](https://docs.rs/deluxe-macros). //! //! This crate is used by [`deluxe_macros`](https://docs.rs/deluxe-macros) to parse its own //! attributes. Code generated by its derive macros also references items from this crate //! re-exported into [`deluxe`](https://docs.rs/deluxe). The functions in [`parse_helpers`] are used //! internally by the derive macros, but can also be used for convenience when manually //! implementing any of the parsing traits. //! //! See the documentation for the [`deluxe`](https://docs.rs/deluxe) crate for a high-level overview //! of how Deluxe works. #![deny(missing_docs)] #![deny(unsafe_code)] #[cfg(feature = "proc-macro")] extern crate proc_macro; mod parse_attributes; pub mod parse_helpers; mod parse_meta; mod small_string; mod util; pub mod validations; pub mod with; pub use parse_attributes::*; pub use parse_meta::*; pub use util::*; pub use proc_macro2::Span; #[doc(hidden)] pub use syn; #[doc(hidden)] pub use { std::{ borrow::Borrow, collections::HashMap, fmt, hash::{Hash, Hasher}, ops, primitive, stringify, }, AsRef, Clone, Default, Eq, IntoIterator, Iterator, Option, PartialEq, }; deluxe-core-0.5.0/parse_attributes.rs000064400000000000000000000577401046102023000160050ustar 00000000000000use crate::Result; use proc_macro2::Span; use syn::spanned::Spanned; /// Parses a structure out of a [`syn::Attribute`] list. /// /// This trait is intended for types that may share a set of matching attributes with other types. pub trait ParseAttributes<'t, T: HasAttributes>: Sized { /// Checks if a given attribute path can be parsed by this type. fn path_matches(path: &syn::Path) -> bool; /// Iterates the attributes from a `T`, parsing any that match. /// /// Implementations should use the [`ref_tokens`](crate::parse_helpers::ref_tokens) helper to /// automatically get references to any matching `TokenStream`s, and then parse them using /// [`parse_struct_attr_tokens`](crate::parse_helpers::parse_struct_attr_tokens). fn parse_attributes(obj: &'t T) -> Result; } /// Extracts a structure out of a [`syn::Attribute`] list. /// /// This trait is intended for types that "consume" attributes from another syntax tree node. pub trait ExtractAttributes: Sized { /// Checks if a given attribute path can be extracted by this type. fn path_matches(path: &syn::Path) -> bool; /// Iterates the attributes from a `T`, extracting and parsing any that match. /// /// Implementations should use the [`take_tokens`](crate::parse_helpers::take_tokens) helper to /// automatically extract any matching `TokenStream`s, and then parse them using /// [`parse_struct_attr_tokens`](crate::parse_helpers::parse_struct_attr_tokens). fn extract_attributes(obj: &mut T) -> Result; } /// Trait for a [`syn`] type containing a list of attributes. /// /// Implementations are provided for all [`syn`] types containing a /// [Vec]<[syn::Attribute]>. pub trait HasAttributes { /// Returns an immutable slice of attributes. fn attrs(&self) -> &[syn::Attribute]; /// Returns a mutable [`Vec`] of attributes. /// /// Returns [`Err`] if the type does not support mutable attributes. fn attrs_mut(&mut self) -> Result<&mut Vec>; } impl HasAttributes for syn::Attribute { #[inline] fn attrs(&self) -> &[syn::Attribute] { std::slice::from_ref(self) } #[inline] fn attrs_mut(&mut self) -> Result<&mut Vec> { Err(syn::Error::new( self.span(), "`HasAttributes::attrs_mut()` not supported on single `syn::Attribute`, try moving it into a `Vec`" )) } } impl HasAttributes for [syn::Attribute] { #[inline] fn attrs(&self) -> &[syn::Attribute] { self } #[inline] fn attrs_mut(&mut self) -> Result<&mut Vec> { let span = self .iter() .fold(None, |s, a| { Some(s.and_then(|s| a.span().join(s)).unwrap_or_else(|| a.span())) }) .unwrap_or_else(Span::call_site); Err(syn::Error::new( span, "`HasAttributes::attrs_mut()` not supported on immutable `[syn::Attribute]`, try using a `Vec`" )) } } impl HasAttributes for Vec { #[inline] fn attrs(&self) -> &[syn::Attribute] { self.as_slice() } #[inline] fn attrs_mut(&mut self) -> Result<&mut Vec> { Ok(self) } } macro_rules! impl_has_attributes { ($(#[$attr:meta])* $ty:ty) => { $(#[$attr])* impl HasAttributes for $ty { #[inline] fn attrs(&self) -> &[syn::Attribute] { &self.attrs } #[inline] fn attrs_mut(&mut self) -> Result<&mut Vec> { Ok(&mut self.attrs) } } }; ($ty:ty, #full) => { impl_has_attributes!( #[cfg(feature = "full")] #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] $ty ); }; } impl_has_attributes!(syn::Arm, #full); impl_has_attributes!(syn::BareFnArg); impl_has_attributes!(syn::ConstParam); impl_has_attributes!(syn::DeriveInput); #[cfg(feature = "full")] #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] impl HasAttributes for syn::Expr { fn attrs(&self) -> &[syn::Attribute] { match self { syn::Expr::Array(expr) => &expr.attrs, syn::Expr::Assign(expr) => &expr.attrs, syn::Expr::Async(expr) => &expr.attrs, syn::Expr::Await(expr) => &expr.attrs, syn::Expr::Binary(expr) => &expr.attrs, syn::Expr::Block(expr) => &expr.attrs, syn::Expr::Break(expr) => &expr.attrs, syn::Expr::Call(expr) => &expr.attrs, syn::Expr::Cast(expr) => &expr.attrs, syn::Expr::Closure(expr) => &expr.attrs, syn::Expr::Const(expr) => &expr.attrs, syn::Expr::Continue(expr) => &expr.attrs, syn::Expr::Field(expr) => &expr.attrs, syn::Expr::ForLoop(expr) => &expr.attrs, syn::Expr::Group(expr) => &expr.attrs, syn::Expr::If(expr) => &expr.attrs, syn::Expr::Index(expr) => &expr.attrs, syn::Expr::Infer(expr) => &expr.attrs, syn::Expr::Let(expr) => &expr.attrs, syn::Expr::Lit(expr) => &expr.attrs, syn::Expr::Loop(expr) => &expr.attrs, syn::Expr::Macro(expr) => &expr.attrs, syn::Expr::Match(expr) => &expr.attrs, syn::Expr::MethodCall(expr) => &expr.attrs, syn::Expr::Paren(expr) => &expr.attrs, syn::Expr::Path(expr) => &expr.attrs, syn::Expr::Range(expr) => &expr.attrs, syn::Expr::Reference(expr) => &expr.attrs, syn::Expr::Repeat(expr) => &expr.attrs, syn::Expr::Return(expr) => &expr.attrs, syn::Expr::Struct(expr) => &expr.attrs, syn::Expr::Try(expr) => &expr.attrs, syn::Expr::TryBlock(expr) => &expr.attrs, syn::Expr::Tuple(expr) => &expr.attrs, syn::Expr::Unary(expr) => &expr.attrs, syn::Expr::Unsafe(expr) => &expr.attrs, syn::Expr::While(expr) => &expr.attrs, syn::Expr::Yield(expr) => &expr.attrs, _ => &[], } } fn attrs_mut(&mut self) -> Result<&mut Vec> { match self { syn::Expr::Array(expr) => Ok(&mut expr.attrs), syn::Expr::Assign(expr) => Ok(&mut expr.attrs), syn::Expr::Async(expr) => Ok(&mut expr.attrs), syn::Expr::Await(expr) => Ok(&mut expr.attrs), syn::Expr::Binary(expr) => Ok(&mut expr.attrs), syn::Expr::Block(expr) => Ok(&mut expr.attrs), syn::Expr::Break(expr) => Ok(&mut expr.attrs), syn::Expr::Call(expr) => Ok(&mut expr.attrs), syn::Expr::Cast(expr) => Ok(&mut expr.attrs), syn::Expr::Closure(expr) => Ok(&mut expr.attrs), syn::Expr::Const(expr) => Ok(&mut expr.attrs), syn::Expr::Continue(expr) => Ok(&mut expr.attrs), syn::Expr::Field(expr) => Ok(&mut expr.attrs), syn::Expr::ForLoop(expr) => Ok(&mut expr.attrs), syn::Expr::Group(expr) => Ok(&mut expr.attrs), syn::Expr::If(expr) => Ok(&mut expr.attrs), syn::Expr::Index(expr) => Ok(&mut expr.attrs), syn::Expr::Let(expr) => Ok(&mut expr.attrs), syn::Expr::Lit(expr) => Ok(&mut expr.attrs), syn::Expr::Loop(expr) => Ok(&mut expr.attrs), syn::Expr::Macro(expr) => Ok(&mut expr.attrs), syn::Expr::Match(expr) => Ok(&mut expr.attrs), syn::Expr::MethodCall(expr) => Ok(&mut expr.attrs), syn::Expr::Paren(expr) => Ok(&mut expr.attrs), syn::Expr::Path(expr) => Ok(&mut expr.attrs), syn::Expr::Range(expr) => Ok(&mut expr.attrs), syn::Expr::Reference(expr) => Ok(&mut expr.attrs), syn::Expr::Repeat(expr) => Ok(&mut expr.attrs), syn::Expr::Return(expr) => Ok(&mut expr.attrs), syn::Expr::Struct(expr) => Ok(&mut expr.attrs), syn::Expr::Try(expr) => Ok(&mut expr.attrs), syn::Expr::TryBlock(expr) => Ok(&mut expr.attrs), syn::Expr::Tuple(expr) => Ok(&mut expr.attrs), syn::Expr::Unary(expr) => Ok(&mut expr.attrs), syn::Expr::Unsafe(expr) => Ok(&mut expr.attrs), syn::Expr::While(expr) => Ok(&mut expr.attrs), syn::Expr::Yield(expr) => Ok(&mut expr.attrs), syn::Expr::Verbatim(_) => Err(syn::Error::new_spanned( self, "`HasAttributes::attrs_mut` not supported with `syn::Expr::Verbatim`", )), _ => Err(syn::Error::new_spanned( self, "`HasAttributes::attrs_mut` encountered unknown `syn::Expr` variant", )), } } } impl_has_attributes!(syn::ExprArray, #full); impl_has_attributes!(syn::ExprAssign, #full); impl_has_attributes!(syn::ExprAsync, #full); impl_has_attributes!(syn::ExprAwait, #full); impl_has_attributes!(syn::ExprBinary, #full); impl_has_attributes!(syn::ExprBlock, #full); impl_has_attributes!(syn::ExprBreak, #full); impl_has_attributes!(syn::ExprCall, #full); impl_has_attributes!(syn::ExprCast, #full); impl_has_attributes!(syn::ExprClosure, #full); impl_has_attributes!(syn::ExprConst, #full); impl_has_attributes!(syn::ExprContinue, #full); impl_has_attributes!(syn::ExprField, #full); impl_has_attributes!(syn::ExprForLoop, #full); impl_has_attributes!(syn::ExprGroup, #full); impl_has_attributes!(syn::ExprIf, #full); impl_has_attributes!(syn::ExprIndex, #full); impl_has_attributes!(syn::ExprInfer, #full); impl_has_attributes!(syn::ExprLet, #full); impl_has_attributes!(syn::ExprLit, #full); impl_has_attributes!(syn::ExprLoop, #full); impl_has_attributes!(syn::ExprMacro, #full); impl_has_attributes!(syn::ExprMatch, #full); impl_has_attributes!(syn::ExprMethodCall, #full); impl_has_attributes!(syn::ExprParen, #full); impl_has_attributes!(syn::ExprPath, #full); impl_has_attributes!(syn::ExprRange, #full); impl_has_attributes!(syn::ExprReference, #full); impl_has_attributes!(syn::ExprRepeat, #full); impl_has_attributes!(syn::ExprReturn, #full); impl_has_attributes!(syn::ExprStruct, #full); impl_has_attributes!(syn::ExprTry, #full); impl_has_attributes!(syn::ExprTryBlock, #full); impl_has_attributes!(syn::ExprTuple, #full); impl_has_attributes!(syn::ExprUnary, #full); impl_has_attributes!(syn::ExprUnsafe, #full); impl_has_attributes!(syn::ExprWhile, #full); impl_has_attributes!(syn::ExprYield, #full); impl_has_attributes!(syn::Field); impl_has_attributes!(syn::FieldPat, #full); impl_has_attributes!(syn::FieldValue, #full); impl_has_attributes!(syn::File, #full); #[cfg(feature = "full")] #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] impl HasAttributes for syn::ForeignItem { fn attrs(&self) -> &[syn::Attribute] { match self { syn::ForeignItem::Fn(item) => &item.attrs, syn::ForeignItem::Static(item) => &item.attrs, syn::ForeignItem::Type(item) => &item.attrs, syn::ForeignItem::Macro(item) => &item.attrs, _ => &[], } } fn attrs_mut(&mut self) -> Result<&mut Vec> { match self { syn::ForeignItem::Fn(item) => Ok(&mut item.attrs), syn::ForeignItem::Static(item) => Ok(&mut item.attrs), syn::ForeignItem::Type(item) => Ok(&mut item.attrs), syn::ForeignItem::Macro(item) => Ok(&mut item.attrs), syn::ForeignItem::Verbatim(_) => Err(syn::Error::new_spanned( self, "`HasAttributes::attrs_mut` not supported with `syn::ForeignItem::Verbatim`", )), _ => Err(syn::Error::new_spanned( self, "`HasAttributes::attrs_mut` encountered unknown `syn::ForeignItem` variant", )), } } } impl_has_attributes!(syn::ForeignItemFn, #full); impl_has_attributes!(syn::ForeignItemMacro, #full); impl_has_attributes!(syn::ForeignItemStatic, #full); impl_has_attributes!(syn::ForeignItemType, #full); #[cfg(feature = "full")] #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] impl HasAttributes for syn::ImplItem { fn attrs(&self) -> &[syn::Attribute] { match self { syn::ImplItem::Const(item) => &item.attrs, syn::ImplItem::Fn(item) => &item.attrs, syn::ImplItem::Type(item) => &item.attrs, syn::ImplItem::Macro(item) => &item.attrs, _ => &[], } } fn attrs_mut(&mut self) -> Result<&mut Vec> { match self { syn::ImplItem::Const(item) => Ok(&mut item.attrs), syn::ImplItem::Fn(item) => Ok(&mut item.attrs), syn::ImplItem::Type(item) => Ok(&mut item.attrs), syn::ImplItem::Macro(item) => Ok(&mut item.attrs), syn::ImplItem::Verbatim(_) => Err(syn::Error::new_spanned( self, "`HasAttributes::attrs_mut` not supported with `syn::ImplItem::Verbatim`", )), _ => Err(syn::Error::new_spanned( self, "`HasAttributes::attrs_mut` encountered unknown `syn::ImplItem` variant", )), } } } impl_has_attributes!(syn::ImplItemConst, #full); impl_has_attributes!(syn::ImplItemFn, #full); impl_has_attributes!(syn::ImplItemMacro, #full); impl_has_attributes!(syn::ImplItemType, #full); #[cfg(feature = "full")] #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] impl HasAttributes for syn::Item { fn attrs(&self) -> &[syn::Attribute] { match self { syn::Item::Const(expr) => &expr.attrs, syn::Item::Enum(expr) => &expr.attrs, syn::Item::ExternCrate(expr) => &expr.attrs, syn::Item::Fn(expr) => &expr.attrs, syn::Item::ForeignMod(expr) => &expr.attrs, syn::Item::Impl(expr) => &expr.attrs, syn::Item::Macro(expr) => &expr.attrs, syn::Item::Mod(expr) => &expr.attrs, syn::Item::Static(expr) => &expr.attrs, syn::Item::Struct(expr) => &expr.attrs, syn::Item::Trait(expr) => &expr.attrs, syn::Item::TraitAlias(expr) => &expr.attrs, syn::Item::Type(expr) => &expr.attrs, syn::Item::Union(expr) => &expr.attrs, syn::Item::Use(expr) => &expr.attrs, _ => &[], } } fn attrs_mut(&mut self) -> Result<&mut Vec> { match self { syn::Item::Const(expr) => Ok(&mut expr.attrs), syn::Item::Enum(expr) => Ok(&mut expr.attrs), syn::Item::ExternCrate(expr) => Ok(&mut expr.attrs), syn::Item::Fn(expr) => Ok(&mut expr.attrs), syn::Item::ForeignMod(expr) => Ok(&mut expr.attrs), syn::Item::Impl(expr) => Ok(&mut expr.attrs), syn::Item::Macro(expr) => Ok(&mut expr.attrs), syn::Item::Mod(expr) => Ok(&mut expr.attrs), syn::Item::Static(expr) => Ok(&mut expr.attrs), syn::Item::Struct(expr) => Ok(&mut expr.attrs), syn::Item::Trait(expr) => Ok(&mut expr.attrs), syn::Item::TraitAlias(expr) => Ok(&mut expr.attrs), syn::Item::Type(expr) => Ok(&mut expr.attrs), syn::Item::Union(expr) => Ok(&mut expr.attrs), syn::Item::Use(expr) => Ok(&mut expr.attrs), syn::Item::Verbatim(_) => Err(syn::Error::new_spanned( self, "`HasAttributes::attrs_mut` not supported with `syn::Item::Verbatim`", )), _ => Err(syn::Error::new_spanned( self, "`HasAttributes::attrs_mut` encountered unknown `syn::Item` variant", )), } } } impl_has_attributes!(syn::ItemConst, #full); impl_has_attributes!(syn::ItemEnum, #full); impl_has_attributes!(syn::ItemExternCrate, #full); impl_has_attributes!(syn::ItemFn, #full); impl_has_attributes!(syn::ItemForeignMod, #full); impl_has_attributes!(syn::ItemImpl, #full); impl_has_attributes!(syn::ItemMacro, #full); impl_has_attributes!(syn::ItemMod, #full); impl_has_attributes!(syn::ItemStatic, #full); impl_has_attributes!(syn::ItemStruct, #full); impl_has_attributes!(syn::ItemTrait, #full); impl_has_attributes!(syn::ItemTraitAlias, #full); impl_has_attributes!(syn::ItemType, #full); impl_has_attributes!(syn::ItemUnion, #full); impl_has_attributes!(syn::ItemUse, #full); impl_has_attributes!(syn::LifetimeParam); impl_has_attributes!(syn::Local, #full); #[cfg(feature = "full")] #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] impl HasAttributes for syn::Pat { fn attrs(&self) -> &[syn::Attribute] { match self { syn::Pat::Const(expr) => &expr.attrs, syn::Pat::Ident(expr) => &expr.attrs, syn::Pat::Lit(expr) => &expr.attrs, syn::Pat::Macro(expr) => &expr.attrs, syn::Pat::Or(expr) => &expr.attrs, syn::Pat::Paren(expr) => &expr.attrs, syn::Pat::Path(expr) => &expr.attrs, syn::Pat::Range(expr) => &expr.attrs, syn::Pat::Reference(expr) => &expr.attrs, syn::Pat::Rest(expr) => &expr.attrs, syn::Pat::Slice(expr) => &expr.attrs, syn::Pat::Struct(expr) => &expr.attrs, syn::Pat::Tuple(expr) => &expr.attrs, syn::Pat::TupleStruct(expr) => &expr.attrs, syn::Pat::Type(expr) => &expr.attrs, syn::Pat::Wild(expr) => &expr.attrs, _ => &[], } } fn attrs_mut(&mut self) -> Result<&mut Vec> { match self { syn::Pat::Const(expr) => Ok(&mut expr.attrs), syn::Pat::Ident(expr) => Ok(&mut expr.attrs), syn::Pat::Lit(expr) => Ok(&mut expr.attrs), syn::Pat::Macro(expr) => Ok(&mut expr.attrs), syn::Pat::Or(expr) => Ok(&mut expr.attrs), syn::Pat::Paren(expr) => Ok(&mut expr.attrs), syn::Pat::Path(expr) => Ok(&mut expr.attrs), syn::Pat::Range(expr) => Ok(&mut expr.attrs), syn::Pat::Reference(expr) => Ok(&mut expr.attrs), syn::Pat::Rest(expr) => Ok(&mut expr.attrs), syn::Pat::Slice(expr) => Ok(&mut expr.attrs), syn::Pat::Struct(expr) => Ok(&mut expr.attrs), syn::Pat::Tuple(expr) => Ok(&mut expr.attrs), syn::Pat::TupleStruct(expr) => Ok(&mut expr.attrs), syn::Pat::Type(expr) => Ok(&mut expr.attrs), syn::Pat::Wild(expr) => Ok(&mut expr.attrs), syn::Pat::Verbatim(_) => Err(syn::Error::new_spanned( self, "`HasAttributes::attrs_mut` not supported with `syn::Pat::Verbatim`", )), _ => Err(syn::Error::new_spanned( self, "`HasAttributes::attrs_mut` encountered unknown `syn::Pat` variant", )), } } } impl_has_attributes!(syn::PatIdent, #full); impl_has_attributes!(syn::PatOr, #full); impl_has_attributes!(syn::PatParen, #full); impl_has_attributes!(syn::PatReference, #full); impl_has_attributes!(syn::PatRest, #full); impl_has_attributes!(syn::PatSlice, #full); impl_has_attributes!(syn::PatStruct, #full); impl_has_attributes!(syn::PatTuple, #full); impl_has_attributes!(syn::PatTupleStruct, #full); impl_has_attributes!(syn::PatType, #full); impl_has_attributes!(syn::PatWild, #full); impl_has_attributes!(syn::Receiver, #full); #[cfg(feature = "full")] #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] impl HasAttributes for syn::TraitItem { fn attrs(&self) -> &[syn::Attribute] { match self { syn::TraitItem::Const(item) => &item.attrs, syn::TraitItem::Fn(item) => &item.attrs, syn::TraitItem::Type(item) => &item.attrs, syn::TraitItem::Macro(item) => &item.attrs, _ => &[], } } fn attrs_mut(&mut self) -> Result<&mut Vec> { match self { syn::TraitItem::Const(item) => Ok(&mut item.attrs), syn::TraitItem::Fn(item) => Ok(&mut item.attrs), syn::TraitItem::Type(item) => Ok(&mut item.attrs), syn::TraitItem::Macro(item) => Ok(&mut item.attrs), syn::TraitItem::Verbatim(_) => Err(syn::Error::new_spanned( self, "`HasAttributes::attrs_mut` not supported with `syn::TraitItem::Verbatim`", )), _ => Err(syn::Error::new_spanned( self, "`HasAttributes::attrs_mut` encountered unknown `syn::TraitItem` variant", )), } } } impl_has_attributes!(syn::TraitItemConst, #full); impl_has_attributes!(syn::TraitItemFn, #full); impl_has_attributes!(syn::TraitItemMacro, #full); impl_has_attributes!(syn::TraitItemType, #full); impl_has_attributes!(syn::TypeParam); impl_has_attributes!(syn::Variadic, #full); impl_has_attributes!(syn::Variant); /// Converts a [`HasAttributes`] type to a stored container value. /// /// This trait is only called by the `#[deluxe(container)`] attribute on fields to properly handle /// conversions to corresponding owned and [`Option`] types. /// /// Custom implementations can be provided if a wrapper for the `syn` type is needed, or if needing /// to transform the `syn` type into something else. When using a custom container type, make sure /// to set `#[deluxe(container(ty = ...))]` on the container field. The /// [`container_from`](Self::container_from) method will be called when deriving /// [`ParseAttributes`], and [`container_from_mut`](Self::container_from_mut) will be called when /// deriving [`ExtractAttributes`]. /// /// ```ignore /// struct IdentWrapper<'t>(&'t syn::Ident); /// /// impl<'t> deluxe::ContainerFrom<'t, syn::ItemFn> for IdentWrapper<'t> { /// #[inline] /// fn container_from(t: &'t syn::ItemFn) -> Self { /// Self(&t.sig.ident) /// } /// } /// /// impl<'t> deluxe::ContainerFrom<'t, syn::ItemStruct> for IdentWrapper<'t> { /// #[inline] /// fn container_from(t: &'t syn::ItemFn) -> Self { /// Self(&t.ident) /// } /// } /// /// // can only be parsed from an `ItemFn` /// #[derive(deluxe::ParseAttributes)] /// struct Func<'t> { /// id: u64, /// #[deluxe(container(ty = syn::ItemFn))] /// container: IdentWrapper<'t>, /// } /// /// // can only be parsed from an `ItemStruct` /// #[derive(deluxe::ParseAttributes)] /// struct Struct<'t> { /// id: u64, /// #[deluxe(container(ty = syn::ItemStruct))] /// container: IdentWrapper<'t>, /// } /// ``` pub trait ContainerFrom<'t, T> { /// Converts a reference to a stored container `T` into this type. #[inline] #[allow(unused)] fn container_from(t: &'t T) -> Self where Self: Sized, { unimplemented!("immutable container not supported for this type") } /// Converts a mutable reference to a stored container `T` into this type. #[inline] #[allow(unused)] fn container_from_mut(t: &'t mut T) -> Self where Self: Sized, { unimplemented!("mutable container not supported for this type") } } impl<'t, T: HasAttributes + Clone> ContainerFrom<'t, T> for T { #[inline] fn container_from(t: &'t T) -> Self { t.clone() } #[inline] fn container_from_mut(t: &'t mut T) -> Self { t.clone() } } impl<'t, T: HasAttributes + Clone> ContainerFrom<'t, T> for Option { #[inline] fn container_from(t: &'t T) -> Self { Some(t.clone()) } #[inline] fn container_from_mut(t: &'t mut T) -> Self { Some(t.clone()) } } impl<'t, T: HasAttributes> ContainerFrom<'t, T> for &'t T { #[inline] fn container_from(t: &'t T) -> Self { t } #[inline] fn container_from_mut(t: &'t mut T) -> Self { t } } impl<'t, T: HasAttributes> ContainerFrom<'t, T> for Option<&'t T> { #[inline] fn container_from(t: &'t T) -> Self { Some(t) } #[inline] fn container_from_mut(t: &'t mut T) -> Self { Some(t) } } impl<'t, T: HasAttributes> ContainerFrom<'t, T> for &'t mut T { #[inline] fn container_from_mut(t: &'t mut T) -> Self { t } } impl<'t, T: HasAttributes> ContainerFrom<'t, T> for Option<&'t mut T> { #[inline] fn container_from_mut(t: &'t mut T) -> Self { Some(t) } } deluxe-core-0.5.0/parse_helpers.rs000064400000000000000000001106431046102023000152510ustar 00000000000000//! Utility functions for parse trait implementations. pub use crate::small_string::SmallString; use crate::{Error, Errors, ParseMetaItem, ParseMode, Result, ToKeyString}; use proc_macro2::{Span, TokenStream, TokenTree}; use quote::ToTokens; use std::{ borrow::{Borrow, Cow}, collections::HashMap, }; pub use syn::{ parse::Nothing, token::{Brace, Bracket, Paren}, }; use syn::{ parse::{ParseBuffer, ParseStream}, spanned::Spanned, Token, }; /// Temporary container for storing parsing state for a single field. #[derive(Copy, Clone, Eq, PartialEq)] pub enum FieldStatus { /// Field is not present or not parsed yet. None, /// Field was present, but had an error during parsing. ParseError, /// Field was found and successfully parsed. Some(T), } impl FieldStatus { /// Converts from `&FieldStatus` to `FieldStatus<&T>`. #[inline] pub const fn as_ref(&self) -> FieldStatus<&T> { match *self { Self::Some(ref x) => FieldStatus::Some(x), Self::ParseError => FieldStatus::ParseError, Self::None => FieldStatus::None, } } /// Returns `b` if the status is `Some`. #[inline] pub fn and(self, b: FieldStatus) -> FieldStatus { match self { Self::Some(_) => b, Self::ParseError => FieldStatus::ParseError, Self::None => FieldStatus::None, } } /// If the status is `Some`, calls `f` with the wrapped value and returns the result. #[inline] pub fn and_then FieldStatus>(self, f: F) -> FieldStatus { match self { Self::Some(x) => f(x), Self::ParseError => FieldStatus::ParseError, Self::None => FieldStatus::None, } } /// Maps a `FieldStatus` to `FieldStatus` by applying a function to a contained value. #[inline] pub fn map U>(self, f: F) -> FieldStatus { match self { Self::Some(x) => FieldStatus::Some(f(x)), Self::ParseError => FieldStatus::ParseError, Self::None => FieldStatus::None, } } /// Returns `true` if the field is a [`FieldStatus::None`] value. #[inline] pub const fn is_none(&self) -> bool { matches!(self, Self::None) } /// Returns `true` if the field is a [`FieldStatus::ParseError`] value. #[inline] pub const fn is_parse_error(&self) -> bool { matches!(self, Self::ParseError) } /// Returns `true` if the field is a [`FieldStatus::Some`] value. #[inline] pub const fn is_some(&self) -> bool { matches!(self, Self::Some(_)) } /// Returns the status if it contains a value, or if it is `None` then returns `b`. #[inline] pub fn or(self, b: FieldStatus) -> FieldStatus { match self { Self::Some(x) => FieldStatus::Some(x), Self::ParseError => FieldStatus::ParseError, Self::None => b, } } /// Returns the status if it contains a value, or if it is `None` then calls `f` and returns /// the result. #[inline] pub fn or_else FieldStatus>(self, f: F) -> FieldStatus { match self { Self::Some(x) => FieldStatus::Some(x), Self::ParseError => FieldStatus::ParseError, Self::None => f(), } } /// Returns the contained [`FieldStatus::Some`] value, consuming the `self` value. /// /// # Panics /// /// Panics if the self value equals [`FieldStatus::None`] or [`FieldStatus::ParseError`]. #[inline] #[track_caller] pub fn unwrap(self) -> T { match self { Self::Some(val) => val, _ => panic!("called `FieldStatus::unwrap()` on a `None` value"), } } /// Returns the contained [`FieldStatus::Some`] value or a provided default. #[inline] pub fn unwrap_or(self, default: T) -> T { match self { Self::Some(x) => x, _ => default, } } /// Returns the contained [`FieldStatus::Some`] value or computes it from a closure. #[inline] pub fn unwrap_or_else T>(self, f: F) -> T { match self { Self::Some(x) => x, _ => f(), } } /// Converts from `FieldStatus>>` to `FieldStatus`. #[inline] pub fn flatten(self) -> FieldStatus where T: Into>, { match self { Self::Some(inner) => inner.into(), Self::ParseError => FieldStatus::ParseError, Self::None => FieldStatus::None, } } /// Parses a named item into this status. #[inline] pub fn parse_named_item(&mut self, name: &str, input: ParseStream, span: Span, errors: &Errors) where T: ParseMetaItem, { self.parse_named_item_with(name, input, span, errors, T::parse_meta_item_named) } /// Parses a named item into this status, using a custom parsing function. #[inline] pub fn parse_named_item_with( &mut self, name: &str, input: ParseStream, span: Span, errors: &Errors, func: F, ) where F: FnOnce(ParseStream, &str, Span) -> Result, { if !self.is_none() { errors.push(span, format_args!("duplicate attribute for `{name}`")); } match errors.push_result(func(input, name, span)) { Some(v) => { if self.is_none() { *self = FieldStatus::Some(v) } } None => { if self.is_none() { *self = FieldStatus::ParseError; } skip_meta_item(input); } } } /// Parses an unnamed item into this status. #[inline] pub fn parse_unnamed_item(&mut self, input: ParseStream, errors: &Errors) where T: ParseMetaItem, { self.parse_unnamed_item_with(input, errors, T::parse_meta_item) } /// Parses an unnamed item into this status, using a custom parsing function. #[inline] pub fn parse_unnamed_item_with(&mut self, input: ParseStream, errors: &Errors, func: F) where F: FnOnce(ParseStream, ParseMode) -> Result, { match errors.push_result(func(input, ParseMode::Unnamed)) { Some(v) => *self = FieldStatus::Some(v), None => { *self = FieldStatus::ParseError; skip_meta_item(input); } } } /// Converts the status to an [`Option`]. #[inline] pub fn into_option(self) -> Option { match self { Self::Some(v) => Some(v), _ => None, } } } impl Default for FieldStatus { #[inline] fn default() -> Self { Self::None } } impl From> for Option { #[inline] fn from(value: FieldStatus) -> Self { value.into_option() } } impl From> for FieldStatus { #[inline] fn from(value: Option) -> Self { match value { Some(v) => Self::Some(v), None => Self::None, } } } mod sealed { pub trait Sealed {} impl Sealed for syn::token::Paren {} impl Sealed for syn::token::Brace {} impl Sealed for syn::token::Bracket {} } /// Helpers for parsing tokens inside a delimiter. /// /// This trait is only implemeted for the [`syn`] delimiter token types. pub trait ParseDelimited: sealed::Sealed { /// Parses a pair of delimiter tokens, and returns a [`ParseBuffer`](syn::parse::ParseBuffer) /// for the tokens inside the delimiter. fn parse_delimited(input: ParseStream) -> Result; /// Parse a stream surrounded by a delimiter. The inner stream is allowed to contain /// a trailing comma. #[inline] fn parse_delimited_with Result>( input: ParseStream, func: F, ) -> Result { let content = Self::parse_delimited(input)?; let result = func(&content)?; parse_eof_or_trailing_comma(&content)?; Ok(result) } /// Parse a [`ParseMetaItem`] surrounded by a delimiter. The inner stream is allowed to contain /// a trailing comma. #[inline] fn parse_delimited_meta_item( input: ParseStream, mode: ParseMode, ) -> Result { Self::parse_delimited_with(input, |inner| T::parse_meta_item_inline(&[inner], mode)) } } impl ParseDelimited for Paren { #[inline] fn parse_delimited(input: ParseStream) -> Result { let content; syn::parenthesized!(content in input); Ok(content) } } impl ParseDelimited for Brace { #[inline] fn parse_delimited(input: ParseStream) -> Result { let content; syn::braced!(content in input); Ok(content) } } impl ParseDelimited for Bracket { #[inline] fn parse_delimited(input: ParseStream) -> Result { let content; syn::bracketed!(content in input); Ok(content) } } /// Checks if a stream is empty or contains only a comma. /// /// Returns `false` if any other tokens are present besides a single comma. #[inline] pub fn peek_eof_or_trailing_comma(input: ParseStream) -> bool { parse_eof_or_trailing_comma(&input.fork()).is_ok() } /// Parses a stream for end-of-stream, or a single comma. /// /// Returns a parse error if any other tokens are present besides a single comma. #[inline] pub fn parse_eof_or_trailing_comma(input: ParseStream) -> Result<()> { if input.peek(Token![,]) { input.parse::()?; } if let Some((tree, _)) = input.cursor().token_tree() { Err(syn::Error::new( tree.span(), format_args!("unexpected token `{tree}`"), )) } else { Ok(()) } } /// Runs a parsing function from an empty token stream. /// /// Creates an empty token stream and then calls `func` on it. Any errors returned from `func` will /// be changed to make them spanned with `span`. #[inline] pub fn parse_empty(span: proc_macro2::Span, func: F) -> Result where F: FnOnce(ParseStream) -> Result, { syn::parse::Parser::parse2(func, TokenStream::new()).map_err(|e| { let mut iter = e.into_iter(); let mut err = Error::new(span, iter.next().unwrap()); for e in iter { err.combine(e); } err }) } /// Parses a [`ParseMetaItem`](crate::ParseMetaItem) inline from an empty token stream. /// /// Creates an empty token stream and then calls /// [`ParseMetaItem::parse_meta_item_inline`](crate::ParseMetaItem::parse_meta_item_inline) on it, /// passing through `mode`. The empty token stream will be created with `span`, so that any /// resulting error messages can be spanned to it. #[inline] pub fn parse_empty_meta_item( span: proc_macro2::Span, mode: ParseMode, ) -> Result { parse_empty(span, |input| T::parse_meta_item_inline(&[input], mode)) } /// Runs a parsing function on the first stream in a stream list, then ensures the rest of the /// streams are empty. pub fn parse_first<'s, T, F, S>(inputs: &[S], mode: ParseMode, func: F) -> Result where F: FnOnce(ParseStream, ParseMode) -> Result, S: Borrow>, { let mut iter = inputs.iter(); if let Some(input) = iter.next() { let input = input.borrow(); let ret = func(input, mode)?; parse_eof_or_trailing_comma(input)?; for next in iter { next.borrow().parse::()?; } Ok(ret) } else { let span = match mode { ParseMode::Named(span) => span, _ => Span::call_site(), }; parse_empty(span, |input| func(input, mode)) } } /// Parses a [`ParseMetaItem`](crate::ParseMetaItem) following a name. /// /// To be used by any parse implementations that expect named attributes. /// /// If the first token is `=`, then calls /// [`ParseMetaItem::parse_meta_item`](crate::ParseMetaItem::parse_meta_item). If the first token /// is a parenthesized group, calls /// [`ParseMetaItem::parse_meta_item_inline`](crate::ParseMetaItem::parse_meta_item_inline) on the /// contents. /// /// If neither a `=` or `(` is parsed, then no additional tokens are consumed, and /// [`ParseMetaItem::parse_meta_item_flag`](crate::ParseMetaItem::parse_meta_item_flag) is called /// with the current stream span. #[inline] pub fn parse_named_meta_item(input: ParseStream, name_span: Span) -> Result { if input.peek(Token![=]) { input.parse::()?; T::parse_meta_item(input, ParseMode::Named(name_span)) } else if input.peek(Paren) { Paren::parse_delimited_meta_item(input, ParseMode::Named(name_span)) } else { T::parse_meta_item_flag(name_span) } } /// The type of a named meta field. /// /// See [`try_parse_named_meta_item`]. #[derive(Debug)] pub enum NamedParse<'p> { /// The field was specified with an equals, .e.g `my_arg = 123`. Equals, /// The field was specified with surrounding parenthesis, .e.g `my_arg(123)`. Paren(ParseBuffer<'p>), /// The field was specified as a flag without a value, .e.g `my_arg`. Flag, } /// Attempts to begin parsing a meta item following a name, returning a value describing what type /// of named meta item this is. /// /// Can be used as an alternative to [`parse_named_meta_item_with`] for custom parsing logic. #[inline] pub fn try_parse_named_meta_item(input: ParseStream) -> Result { if input.peek(Token![=]) { input.parse::()?; Ok(NamedParse::Equals) } else if input.peek(Paren) { let content = Paren::parse_delimited(input)?; Ok(NamedParse::Paren(content)) } else { Ok(NamedParse::Flag) } } /// Parses a [`ParseMetaItem`](crate::ParseMetaItem) following a name, using custom parse /// functions. /// /// Equivalent to [`parse_named_meta_item`], but the functions are passed directly instead of using /// the implementations from a trait. Used for parsing enum variants. For parsing with a custom /// module path, use [`parse_named_meta_item_with!`](crate::parse_named_meta_item_with!). #[inline] pub fn parse_named_meta_item_with( input: ParseStream, name_span: Span, parse: I, parse_inline: II, parse_flag: IF, ) -> Result where I: FnOnce(ParseStream, ParseMode) -> Result, II: FnOnce(&[ParseStream], ParseMode) -> Result, IF: FnOnce(proc_macro2::Span) -> Result, { if input.peek(Token![=]) { input.parse::()?; parse(input, ParseMode::Named(name_span)) } else if input.peek(Paren) { let content = Paren::parse_delimited(input)?; let result = parse_inline(&[&content], ParseMode::Named(name_span))?; parse_eof_or_trailing_comma(&content)?; Ok(result) } else { parse_flag(name_span) } } /// Parses a [`ParseMetaItem`](crate::ParseMetaItem) following a name, using a custom parse /// module. /// /// Equivalent to [`parse_named_meta_item_with`](fn@parse_named_meta_item_with), but the functions /// are taken from a module path. Can be used to implement additional parsing strategies for types /// that already implement [`ParseMetaItem`](crate::ParseMetaItem), or for generic types. /// Some common parsers are available in the [`with`](crate::with) module. /// /// The first argument is a [`syn::parse::ParseStream`], the second argument is the /// [`Span`](proc_macro2::Span) of the name tokens, and the third argument is a module path. /// Modules are required to implement the functions `parse_meta_item`, `parse_meta_item_inline`, /// and `parse_meta_item_flag`, even if just to return an error. The signatures of these functions /// should match the equivalent functions in [`ParseMetaItem`](crate::ParseMetaItem). /// /// `parse_meta_item_flag` implementations can call [`flag_disallowed_error`] for a standard error /// if flags are not supported by the target type. #[macro_export] macro_rules! parse_named_meta_item_with { ($input:expr, $name_span:expr, $($path:tt)* $(,)?) => { $crate::parse_helpers::parse_named_meta_item_with( $input, $name_span, $($path)* :: parse_meta_item, |inputs, mode| $($path)* :: parse_meta_item_inline(inputs, mode), $($path)* :: parse_meta_item_flag, ) }; } /// Parses an inline fixed-length tuple struct from a stream. /// /// `len` is the maximum number of items to parse. Each stream in `inputs` will be parsed in order, /// calling `func` for every field with three arguments: the current stream, a slice containing the /// current stream and all following streams, and the index of the current field starting from `0`. /// Commas will be parsed between each field. /// /// Returns the number of items that were successfully parsed, or [`Err`] on any parsing errors or /// the first failure of `func`. A trailing comma will be consumed only if the return value is less /// than `len`. #[inline] pub fn parse_tuple_struct<'s, F, S>(mut inputs: &[S], len: usize, mut func: F) -> Result where S: Borrow>, F: FnMut(ParseStream, &[S], usize) -> Result<()>, { let mut counter = 0; while let Some(input) = inputs.first() { let input = input.borrow(); loop { if input.is_empty() { break; } if counter >= len { return Ok(counter); } func(input, inputs, counter)?; counter += 1; if !input.is_empty() && counter < len { input.parse::()?; } } inputs = &inputs[1..]; } Ok(counter) } /// Parses a path allowing any keywords as identifiers, and containing no path arguments. pub fn parse_any_path(input: ParseStream) -> Result { Ok(syn::Path { leading_colon: input.parse()?, segments: { let mut segments = syn::punctuated::Punctuated::new(); loop { if input.cursor().ident().is_none() { break; } let ident = ::parse_any(input)?; segments.push_value(syn::PathSegment::from(ident)); if !input.peek(syn::Token![::]) { break; } let punct = input.parse()?; segments.push_punct(punct); } if segments.is_empty() { return Err(input.error("expected path")); } else if segments.trailing_punct() { return Err(input.error("expected path segment")); } segments }, }) } /// Parses an inline struct with named fields from a stream. /// /// Each stream in `inputs` will be parsed in order, first parsing a path by calling /// [`syn::Path::parse_mod_style`]. Then, `func` will be called for every field with three /// arguments: the current stream, the name of the field as a string, and the span of the field /// name. Commas will be parsed between each field. A trailing comma will always be consumed. /// Callers will typically check the field name for a match, and then call /// [`parse_named_meta_item`] or [`parse_named_meta_item_with`](fn@parse_named_meta_item_with). /// /// Callers will usually want to use [`check_unknown_attribute`] and [`skip_meta_item`] when /// encountering any unknown fields. /// /// Returns [`Err`] on any parsing errors or the first failure of `func`. #[inline] pub fn parse_struct<'s, F, S>(inputs: &[S], mut func: F) -> Result<()> where S: Borrow>, F: FnMut(ParseStream, &str, Span) -> Result<()>, { for input in inputs { let input = input.borrow(); loop { if input.is_empty() { break; } let path = input.call(parse_any_path)?; func(input, key_to_string(&path).as_str(), path.span())?; if !input.is_empty() { input.parse::()?; } } } Ok(()) } /// Skips over items in a [`ParseStream`](syn::parse::ParseStream) until a comma is reached. /// /// Consumes all tokens up until the comma. Does not consume the comma. #[inline] pub fn skip_meta_item(input: ParseStream) { input .step(|cursor| { let mut cur = *cursor; while let Some((tt, next)) = cur.token_tree() { if let TokenTree::Punct(punct) = tt { if punct.as_char() == ',' { break; } } cur = next; } Ok(((), cur)) }) .ok(); } /// Consumes all tokens left in a list of streams. #[inline] pub fn skip_all<'s, S>(inputs: &[S]) where S: Borrow>, { for input in inputs { input .borrow() .step(|cursor| { let mut cur = *cursor; while let Some((_, next)) = cur.token_tree() { cur = next; } Ok(((), cur)) }) .ok(); } } #[inline] fn parse_tokens Result>(input: TokenStream, func: F) -> Result { let errors = Errors::new(); let mut ret = None; syn::parse::Parser::parse2( |stream: ParseStream<'_>| { ret = match func(stream) { Ok(ret) => Some(ret), Err(err) => { errors.push_syn(err); None } }; if let Err(err) = parse_eof_or_trailing_comma(stream) { errors.push_syn(err); } Ok(()) }, input, )?; errors.check()?; Ok(ret.unwrap()) } /// Parses a collection of [`ToTokens`](quote::ToTokens) values into a struct with named fields. /// /// Should be used from [`ParseAttributes`](crate::ParseAttributes) or /// [`ExtractAttributes`](crate::ExtractAttributes) implementations, which should respectively call /// [`ref_tokens`] or [`take_tokens`] before passing any token streams into this function. /// /// Each stream will be parsed for a parenthesized group. Then, `func` will be called with a list /// of every [`ParseBuffer`](syn::parse::ParseBuffer) inside these groups, and the corresponding /// [`Span`](proc_macro2::Span) for the path of that group. An end-of-stream (or trailing comma /// followed by end-of-stream) will be consumed from each buffer after `func` returns. Callers /// should ensure the success path consumes all other tokens in each buffer. /// /// Structs with named fields or tuple structs should be parsed by respectively calling /// [`parse_struct`] or [`parse_tuple_struct`]. /// /// Returns a parse error if any of the streams failed to parse. Otherwise, passes through the /// return value from `func`. pub fn parse_struct_attr_tokens(inputs: I, func: F) -> Result where T: quote::ToTokens, I: IntoIterator, Span)>, F: FnOnce(&[ParseBuffer], &[(SmallString<'static>, Span)]) -> Result, { fn parse_next( mut iter: I, buffers: Vec, mut paths: Vec<(SmallString<'static>, Span)>, func: F, ) -> Result where T: quote::ToTokens, I: Iterator, Span)>, F: FnOnce(&[ParseBuffer], &[(SmallString<'static>, Span)]) -> Result, { if let Some((tokens, name, span)) = iter.next() { let tokens = tokens.into_token_stream(); parse_tokens(tokens, |stream| { let mut buffers: Vec = buffers; // move to shrink the lifetime if stream.is_empty() { buffers.push(stream.fork()); } else { let content = Paren::parse_delimited(stream)?; buffers.push(content); } paths.push((name, span)); parse_next(iter, buffers, paths, func) }) } else { let r = func(&buffers, &paths)?; for buffer in buffers { parse_eof_or_trailing_comma(&buffer)?; } Ok(r) } } parse_next(inputs.into_iter(), Vec::new(), Vec::new(), func) } /// Converts a [`ToKeyString`] to a [`SmallString`]. pub fn key_to_string(t: &T) -> SmallString<'static> { struct FormatKey<'a, A>(&'a A); impl<'a, A: ToKeyString> std::fmt::Display for FormatKey<'a, A> { #[inline] fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { self.0.fmt_key_string(f) } } let mut ss = SmallString::new(); std::fmt::write(&mut ss, format_args!("{}", FormatKey(t))).unwrap(); ss } /// Concatenates two path strings. /// /// If `prefix` is empty, returns `path`. Otherwise, joins `prefix` and `path` together with `::` /// as a separator. If `prefix` ends with `::` then no additional `::` will be added. #[inline] pub fn join_path<'path>(prefix: &str, path: &'path str) -> Cow<'path, str> { if prefix.is_empty() { Cow::Borrowed(path) } else if prefix.ends_with("::") { format!("{prefix}{path}").into() } else { format!("{prefix}::{path}").into() } } /// Concatenates two path prefix strings. /// /// Similar to [`join_path`], but all returned strings will have `::` appended to the end of them. /// The returned string will be suitable to pass to [`str::strip_prefix`] after calling /// [`key_to_string`]. #[inline] pub fn join_prefix(prefix: &str, path: &str) -> String { if prefix.is_empty() { let mut out = path.to_string(); out.push_str("::"); out } else { let mut out = prefix.to_string(); if !prefix.ends_with("::") { out.push_str("::"); } out.push_str(path); out.push_str("::"); out } } /// Calls [`join_path`] on all paths in a slice. #[inline] pub fn join_paths<'s>(prefix: &str, paths: &[&'s str]) -> Vec> { paths.iter().map(|p| join_path(prefix, p)).collect() } #[inline] #[doc(hidden)] pub fn extend_from_owned<'s>(vec: &mut Vec<&'s str>, owned: &'s [Cow<'s, str>]) { vec.extend(owned.iter().map(|p| p.as_ref())); } /// Checks if a path matches a list of names. /// /// Each string in `segs` is checked against `path.segments[..].ident`. /// /// Returns `false` if the paths are different, or if any segments have generic arguments. pub fn path_matches(path: &syn::Path, segs: &[&str]) -> bool { if path.segments.len() != segs.len() { return false; } for (i, seg) in path.segments.iter().enumerate() { if !matches!(seg.arguments, syn::PathArguments::None) { return false; } if seg.ident != segs[i] { return false; } } true } /// Checks if all keys are contained in a set of paths. /// /// A key is a list of names that could be used for a field. Returns `true` if each key has at /// least one name present in `paths`. #[inline] pub fn has_paths(paths: &HashMap, Span>, keys: &[&[&str]]) -> bool { keys.iter() .all(|ks| ks.iter().any(|i| paths.contains_key(*i))) } /// Removes some paths from a path hashmap. /// /// `keys` is an array of `(prefix, keys)` tuples. The prefix will be be prepended to the key and /// then the resulting string will be removed from `paths`. pub fn remove_paths(paths: &mut HashMap, Span>, keys: &[(&str, &[&str])]) { for (prefix, ks) in keys { for path in *ks { let path = join_path(prefix, path); paths.remove(path.as_ref()); } } } /// Appends errors if any keys are found in a list of paths. /// /// `keys` is an array of `(prefix, keys)` tuples. The prefix will be be prepended to the key and /// then checked against `paths`. For every key found, a "disallowed" error will be appended to /// `errors`. pub fn disallow_paths( paths: &HashMap, Span>, keys: &[(&str, &[&str])], cur: &str, errors: &Errors, ) { for (prefix, ks) in keys { for path in *ks { let path = join_path(prefix, path); if let Some(span) = paths.get(path.as_ref()) { errors.push( *span, format_args!("`{path}` not allowed with variant `{cur}`"), ); } } } } /// Creates a span encompassing a collection of [`ParseStream`](syn::parse::ParseStream)s. /// /// Returns a span joined togther from each item in the slice, using /// [`Span::join`](proc_macro2::Span::join). #[inline] pub fn inputs_span<'s, S: Borrow>>(inputs: &[S]) -> Span { let mut iter = inputs.iter(); let first = iter.next(); if let Some(input) = first { let mut span = input.borrow().span(); for next in iter { if let Some(joined) = span.join(next.borrow().span()) { span = joined; } } span } else { Span::call_site() } } /// Returns an error with a "missing required field" message. /// /// The error will be spanned to `span`. #[inline] pub fn missing_field_error(name: &str, span: Span) -> Error { syn::Error::new(span, format_args!("missing required field {name}")) } /// Returns an error with a "unexpected flag" message. /// /// The error will be spanned to `span`. #[inline] pub fn flag_disallowed_error(span: Span) -> Error { syn::Error::new(span, "unexpected flag, expected `=` or parentheses") } /// Returns an error with an "unknown field" message. /// /// `path` is the name of the unknown field that will be included in the error message. The names /// of allowed fields can be passed in `fields`. If any fields are a close enough match to `path`, /// then a "did you mean" message will be appended to the end of the error message. The error will /// be spanned to `span`. pub fn unknown_error(path: &str, span: Span, fields: &[&str]) -> Error { let mut closest = None; for field in fields { let sim = strsim::jaro_winkler(path, field); if sim > 0.8 && closest.as_ref().map(|(s, _)| sim > *s).unwrap_or(true) { closest = Some((sim, *field)); } } if let Some((_, closest)) = closest { Error::new( span, format!("unknown field `{path}`, did you mean `{closest}`?"), ) } else { Error::new(span, format!("unknown field `{path}`")) } } /// Appends an error if a field is not included in a list of fields. /// /// If `path` is not present in `fields` then an error will be appended to `errors`. The error will /// be spanned to `span`. Returns `true` if an error was appended. #[inline] pub fn check_unknown_attribute(path: &str, span: Span, fields: &[&str], errors: &Errors) -> bool { if !fields.contains(&path) { errors.push_syn(unknown_error(path, span, fields)); return true; } false } /// Returns an iterator of pairs of [`TokenStream`](proc_macro2::TokenStream)s and the /// corresponding path [`Span`](proc_macro2::Span) from a matched /// [`ParseAttributes`](crate::ParseAttributes). #[inline] pub fn ref_tokens<'t, P, T>( input: &'t T, ) -> impl Iterator, Span)> + 't where P: crate::ParseAttributes<'t, T>, T: crate::HasAttributes, { T::attrs(input).iter().filter_map(|a| { P::path_matches(a.path()).then(|| { let value = match &a.meta { syn::Meta::Path(_) => Default::default(), syn::Meta::List(list) => proc_macro2::TokenTree::Group(proc_macro2::Group::new( match list.delimiter { syn::MacroDelimiter::Paren(_) => proc_macro2::Delimiter::Parenthesis, syn::MacroDelimiter::Brace(_) => proc_macro2::Delimiter::Brace, syn::MacroDelimiter::Bracket(_) => proc_macro2::Delimiter::Bracket, }, list.tokens.clone(), )) .into(), syn::Meta::NameValue(nv) => nv.value.to_token_stream(), }; (value, key_to_string(a.path()), a.path().span()) }) }) } /// Returns an iterator of [`TokenStream`](proc_macro2::TokenStream)s and the corresponding path /// [`Span`](proc_macro2::Span) from a matched [`ExtractAttributes`](crate::ExtractAttributes). /// /// All matching attributes will be removed from `input`. The resulting /// [`TokenStream`](proc_macro2::TokenStream)s are moved into the iterator. #[inline] pub fn take_tokens( input: &mut T, ) -> Result, Span)>> where E: crate::ExtractAttributes, T: crate::HasAttributes, { let attrs = T::attrs_mut(input)?; let mut tokens = Vec::new(); let mut index = 0; while index < attrs.len() { if E::path_matches(attrs[index].path()) { let attr = attrs.remove(index); let span = attr.path().span(); let key = key_to_string(attr.path()); let value = match attr.meta { syn::Meta::Path(_) => Default::default(), syn::Meta::List(list) => proc_macro2::TokenTree::Group(proc_macro2::Group::new( match list.delimiter { syn::MacroDelimiter::Paren(_) => proc_macro2::Delimiter::Parenthesis, syn::MacroDelimiter::Brace(_) => proc_macro2::Delimiter::Brace, syn::MacroDelimiter::Bracket(_) => proc_macro2::Delimiter::Bracket, }, list.tokens, )) .into(), syn::Meta::NameValue(nv) => nv.value.into_token_stream(), }; tokens.push((value, key, span)); } else { index += 1; } } Ok(tokens.into_iter()) } /// Gets the first [`Span`](proc_macro2::Span) in a list of path names and spans. /// /// Returns [`Span::call_site`](proc_macro2::Span::call_site) if the slice is empty. #[inline] pub fn first_span(spans: &[(SmallString<'static>, Span)]) -> Span { spans.first().map(|s| s.1).unwrap_or_else(Span::call_site) } /// Gets the first string in a list of path names and spans. /// /// Returns [`None`] if the slice is empty. #[inline] pub fn first_path_name<'a>(spans: &'a [(SmallString<'static>, Span)]) -> Option<&'a str> { spans.first().map(|s| s.0.as_str()) } /// Forks all streams in a [`ParseStream`](syn::parse::ParseStream) slice into a new [`Vec`] of /// owned [`ParseBuffer`](syn::parse::ParseBuffer)s. /// /// Used to copy the initial states in a list of streams before calling [`parse_struct`]. #[inline] pub fn fork_inputs<'s, S>(inputs: &[S]) -> Vec> where S: Borrow>, { inputs.iter().map(|s| s.borrow().fork()).collect() } /// Appends a "too many enum variants" error. /// /// The error will be appended to `errors` and spanned to `span`. `prefix` will be appended to the /// variant names. #[inline] pub fn only_one_variant(span: Span, prefix: &str, (va, vb): (&str, &str), errors: &Errors) { errors.push( span, std::format_args!( "expected only one enum variant, got `{}` and `{}`", join_path(prefix, va), join_path(prefix, vb) ), ); } /// Appends a "enum variant expected" error message. /// /// `variants` is a list of variants, where each variant is a list of fields, where each field is a /// list of names that can be used for that field. /// /// The error will be appended to `errors` and spanned to `span`. `prefix` will be appended to the /// variant names. #[inline] pub fn variant_required(span: Span, prefix: &str, variants: &[&[&[&str]]], errors: &Errors) { errors.push( span, std::format_args!( "expected one of {}", variants .iter() .map(|keys| { let mut iter = keys.iter().map(|idents| { idents .iter() .map(|i| join_path(prefix, i).into_owned()) .collect::>() .join("|") }); if keys.len() == 1 { iter.next().unwrap() } else { format!("({})", iter.collect::>().join(", ")) } }) .collect::>() .join(", ") ), ); } deluxe-core-0.5.0/parse_meta.rs000064400000000000000000001613751046102023000145450ustar 00000000000000use crate::{parse_helpers::*, Errors, Result}; use proc_macro2::Span; use quote::ToTokens; use std::{ borrow::Borrow, collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}, fmt::{self, Write}, hash::Hash, }; use syn::{ parse::{Nothing, Parse, ParseBuffer, ParseStream, Peek}, punctuated::Punctuated, Token, }; /// The context a meta item parser is operating in. #[derive(Debug, Clone, Copy)] pub enum ParseMode { /// Named context, corresponding to [`syn::FieldsNamed`]. /// /// Contains the [`Span`](proc_macro2::Span) of the name tokens. Named(Span), /// Unnamed context, corresponding to [`syn::FieldsUnnamed`]. Unnamed, } impl ParseMode { /// Converts `self` to a [`Self::Named`]. /// /// If `self` is [`Unnamed`](Self::Unnamed), uses the [`Span`](proc_macro2::Span) from `input`. #[inline] pub fn to_named(&self, input: ParseStream) -> Self { match self { Self::Unnamed => Self::Named(input.span()), m => *m, } } /// Gets the stored [`Span`](proc_macro2::Span). /// /// If `self` is [`Unnamed`](Self::Unnamed), returns [`None`]. #[inline] pub fn named_span(&self) -> Option { match self { Self::Named(s) => Some(*s), _ => None, } } /// Gets the stored [`Span`](proc_macro2::Span). /// /// If `self` is [`Unnamed`](Self::Unnamed), returns the [`Span`](proc_macro2::Span) from /// `input`. #[inline] pub fn to_span(&self, input: ParseStream) -> Span { self.named_span().unwrap_or_else(|| input.span()) } /// Gets the stored [`Span`](proc_macro2::Span). /// /// If `self` is [`Unnamed`](Self::Unnamed), returns [inputs_span]\(inputs). #[inline] pub fn to_full_span<'s, S: Borrow>>(&self, inputs: &[S]) -> Span { self.named_span().unwrap_or_else(|| inputs_span(inputs)) } } /// Base trait for parsing a single field out of [`syn::parse::ParseStream`]. /// /// This trait is conceptually similar to [`syn::parse::Parse`], but with a few key differences: /// - It can tell when an item is being parsed from a tuple struct or a struct with named fields. /// - It can be parsed "inline" by containers that consume the surrounding delimiters. /// - It can be parsed as a "flag" by containers that allow empty fields. /// - It can provide custom parsing when it is used with a named attribute. /// /// Implementations are provided for all [primitives](std::primitive) and [standard /// collections](std::collections), as well as for tuples, arrays, and for most parseable /// node structures in [`syn`]. pub trait ParseMetaItem: Sized { /// Parse the item from the tokens in `input`. /// /// If the item can contain commas, the whole item should be wrapped in a pair of delimiters. /// The stream should should not parse any trailing commas or additional tokens past the end of /// the item. /// /// The `_mode` argument describes whether this item is being parsed in a named or an unnamed /// context. This argument will rarely be used. It only should be checked when an item uses /// [`Self::parse_meta_item_flag`] to provide shorthand for a default enum value, and wants to /// avoid parsing it in a named context. In an unnamed context, both values will still need to /// be parsed. See the source to the /// [implementation](ParseMetaItem#impl-ParseMetaItem-for-Option) of this trait on /// [Option]<T> for an example of when to check `_mode`. fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result; /// Parse the item in an inline context. /// /// The stream can consume any number of commas. Items with a finite length should not consume /// a trailing comma. See [`Self::parse_meta_item`] for a description of the `_mode` argument. /// /// This function should be implemented for custom data structures, but is not intended to be /// called by a parser of a structure containing it as a field. Instead, the custom data /// structure should implement [`ParseMetaFlatUnnamed::parse_meta_flat_unnamed`], /// [`ParseMetaFlatNamed::parse_meta_flat_named`], or /// [`ParseMetaRest::parse_meta_rest`]. Then, the implementation of this /// function should delegate to one of those methods. /// /// The default implementation directly calls [`Self::parse_meta_item`]. #[inline] fn parse_meta_item_inline<'s, S: Borrow>>( inputs: &[S], _mode: ParseMode, ) -> Result { parse_first(inputs, _mode, Self::parse_meta_item) } /// Parses an empty flag value. /// /// Only called when parsing a struct with named fields, with no value given after the field /// name. `_span` is the span of the token following the name. /// /// The default implementation returns an error. #[inline] fn parse_meta_item_flag(_span: Span) -> Result { Err(flag_disallowed_error(_span)) } /// Parses the item following a name. /// /// Only called when parsing a struct with named fields. Normally implementations will not need /// to override this method. /// /// The default implementation simply calls /// [`parse_helpers::parse_named_meta_item`](crate::parse_helpers::parse_named_meta_item). #[inline] fn parse_meta_item_named(input: ParseStream, _name: &str, span: Span) -> Result { parse_named_meta_item(input, span) } /// Fallback for when a required item is missing. /// /// Only called when a required (non-default) field was omitted from a parsed attribute. /// Implementations on types that implement [`Default`] will most likely want to return /// [Ok]\([Default::default]\(\)\) here. /// /// The default implementation returns an error. #[inline] fn missing_meta_item(name: &str, span: Span) -> Result { Err(missing_field_error(name, span)) } } /// Parses a meta item for a structure with unnamed fields. /// /// Automatically implemented by `derive(ParseMetaItem)` for tuple /// structures, but can also be manually implemented for array-like types. /// /// A parent structure that wants to flatten items of this type into its own fields should call /// [`Self::parse_meta_flat_unnamed`]. pub trait ParseMetaFlatUnnamed: Sized { /// Returns the number of fields in this structure. /// /// Returns [`None`] if it can parse any number of fields. /// /// Currently, this is only useful when generating error messages. fn field_count() -> Option; /// Parse the item from a group of inline contexts. /// /// The streams can consume any number of commas. The streams in `inputs` should not be /// considered contiguous, but should imply a trailing comma if one is not present. /// Implementations should parse each stream in order, continuing to the next stream when the /// current one is exhausted. /// /// If the structure can take any number of fields, it should parse all streams to the end and /// ensure that a traiing comma is parsed. /// /// If the structure contains a finite number of fields, then trailing comma should not be /// consumed. Once all fields are parsed, the function should return, without any further /// modifications to the current stream or any following streams. /// /// `index` is the starting offset into the currently parsing tuple. It should be used as a /// base when generating error messages. fn parse_meta_flat_unnamed<'s, S: Borrow>>( inputs: &[S], _mode: ParseMode, index: usize, ) -> Result; } /// Parses a meta item for a structure with named fields. /// /// Automatically implemented by `derive(ParseMetaItem)` for structures with named fields, and for /// enums. /// /// A parent structure that wants to flatten items of this type into its own fields should call /// [`Self::parse_meta_flat_named`]. pub trait ParseMetaFlatNamed: Sized { /// Returns an array specifying all optional and required fields accepted by this structure. fn field_names() -> &'static [&'static str]; /// Parse the item from a group of inline named contexts. /// /// The streams can consume any number of commas. The streams in `inputs` should not be /// considered contiguous, but should imply a trailing comma if one is not present. /// Implementations should parse each stream in order, continuing to the next stream when the /// current one is exhausted. /// /// All streams should be parsed to the end, skipping over unknown fields, and consuming a /// trailing comma if present. The [`crate::parse_helpers::parse_struct`] helper should be used /// for convenience. `prefix` should be stripped off the names of all fields before pattern /// matching on them. /// /// If `validate` is true, then an error should be generated upon encountering any /// fields not in [`Self::field_names`]. fn parse_meta_flat_named<'s, S: Borrow>>( inputs: &[S], _mode: ParseMode, prefix: &str, validate: bool, ) -> Result; /// A flag noting if this parser will consume all unknown fields. /// /// Should be set to `true` only for structures containing a `#[deluxe(rest)]` field. const ACCEPTS_ALL: bool = false; } /// Parses a meta item for a structure with named fields that concatenates all matching items. /// /// Should be implemented on collection types. Implementations are provided for the common /// collection types in [`std`]. pub trait ParseMetaAppend: Sized { /// Parse the item from a group of inline named contexts. /// /// Fields with names matching any path in `paths` will be appended. Non-matching fields should /// be skipped with [`crate::parse_helpers::skip_meta_item`]. fn parse_meta_append<'s, S, I, P>(inputs: &[S], paths: I) -> Result where S: Borrow>, I: IntoIterator, I::IntoIter: Clone, P: AsRef; } /// Parses a meta item for a structure with named fields that consumes all fields. /// /// Should be implemented on map types. Implementations are provided for the common map types in /// [`std`]. pub trait ParseMetaRest: Sized { /// Parse the item from a group of inline named contexts. /// /// Fields with names in `exclude` should be should be skipped with /// [`crate::parse_helpers::skip_meta_item`]. fn parse_meta_rest<'s, S: Borrow>>( inputs: &[S], exclude: &[&str], ) -> Result; } /// A trait for converting an attribute key to a string. /// /// Used for performing path comparisons and for constructing error messages when using arbitrary /// types as attribute keys. Currently only used by the [`ParseMetaItem`] implementations for /// [`BTreeMap`] and [`HashMap`]. pub trait ToKeyString: Sized { /// Formats the given value as a key string. fn fmt_key_string(&self, f: &mut fmt::Formatter) -> fmt::Result; /// Runs function `f` with the key converted to a `&str`. /// /// Can be specialized by values that can pass a cheaply borrowed `&str`. The default /// implementation calls `f` with the result from [`key_to_string`]. fn with_key_string(&self, f: impl FnOnce(&str) -> R) -> R { f(&key_to_string(self)) } } macro_rules! impl_parse_meta_item_primitive { ($ty:ty, $lit:ty, $conv:ident) => { impl ParseMetaItem for $ty { #[inline] fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { impl_parse_meta_item_primitive!(@conv input, _mode, $lit, $conv) } } impl ToKeyString for $ty { #[inline] fn fmt_key_string(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self, f) } } }; (@conv $input:ident, $mode:ident, $lit:ty, base10_parse) => { $input.parse::<$lit>()?.base10_parse() }; (@conv $input:ident, $mode:ident, $lit:ty, value) => { Ok($input.parse::<$lit>()?.value()) }; (@conv $input:ident, $mode:ident, $lit:ty, from_str) => { $crate::with::from_str::parse_meta_item($input, $mode) }; } impl_parse_meta_item_primitive!(i8, syn::LitInt, base10_parse); impl_parse_meta_item_primitive!(i16, syn::LitInt, base10_parse); impl_parse_meta_item_primitive!(i32, syn::LitInt, base10_parse); impl_parse_meta_item_primitive!(i64, syn::LitInt, base10_parse); impl_parse_meta_item_primitive!(i128, syn::LitInt, base10_parse); impl_parse_meta_item_primitive!(isize, syn::LitInt, base10_parse); impl ParseMetaItem for u8 { #[inline] fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { let lookahead = input.lookahead1(); if lookahead.peek(syn::LitByte) { Ok(input.parse::()?.value()) } else if lookahead.peek(syn::LitInt) { Ok(input.parse::()?.base10_parse()?) } else { Err(lookahead.error()) } } } impl_parse_meta_item_primitive!(u16, syn::LitInt, base10_parse); impl_parse_meta_item_primitive!(u32, syn::LitInt, base10_parse); impl_parse_meta_item_primitive!(u64, syn::LitInt, base10_parse); impl_parse_meta_item_primitive!(u128, syn::LitInt, base10_parse); impl_parse_meta_item_primitive!(usize, syn::LitInt, base10_parse); impl_parse_meta_item_primitive!(f32, syn::LitFloat, base10_parse); impl_parse_meta_item_primitive!(f64, syn::LitFloat, base10_parse); impl ParseMetaItem for bool { #[inline] fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { Ok(input.parse::()?.value()) } #[inline] fn parse_meta_item_flag(_: Span) -> Result { Ok(true) } } impl_parse_meta_item_primitive!(std::num::NonZeroI8, syn::LitInt, base10_parse); impl_parse_meta_item_primitive!(std::num::NonZeroI16, syn::LitInt, base10_parse); impl_parse_meta_item_primitive!(std::num::NonZeroI32, syn::LitInt, base10_parse); impl_parse_meta_item_primitive!(std::num::NonZeroI64, syn::LitInt, base10_parse); impl_parse_meta_item_primitive!(std::num::NonZeroI128, syn::LitInt, base10_parse); impl_parse_meta_item_primitive!(std::num::NonZeroIsize, syn::LitInt, base10_parse); impl_parse_meta_item_primitive!(std::num::NonZeroU8, syn::LitInt, base10_parse); impl_parse_meta_item_primitive!(std::num::NonZeroU16, syn::LitInt, base10_parse); impl_parse_meta_item_primitive!(std::num::NonZeroU32, syn::LitInt, base10_parse); impl_parse_meta_item_primitive!(std::num::NonZeroU64, syn::LitInt, base10_parse); impl_parse_meta_item_primitive!(std::num::NonZeroU128, syn::LitInt, base10_parse); impl_parse_meta_item_primitive!(std::num::NonZeroUsize, syn::LitInt, base10_parse); impl_parse_meta_item_primitive!(char, syn::LitChar, value); impl ParseMetaItem for String { #[inline] fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { Ok(input.parse::()?.value()) } } impl ToKeyString for String { #[inline] fn fmt_key_string(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self, f) } #[inline] fn with_key_string(&self, f: impl FnOnce(&str) -> R) -> R { f(self) } } impl ParseMetaItem for std::path::PathBuf { #[inline] fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { Ok(Self::from(input.parse::()?.value())) } } impl ToKeyString for std::path::PathBuf { #[inline] fn fmt_key_string(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(&self.to_string_lossy()) } #[inline] fn with_key_string(&self, f: impl FnOnce(&str) -> R) -> R { f(&self.to_string_lossy()) } } impl ParseMetaItem for std::ffi::OsString { #[inline] fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { Ok(Self::from(input.parse::()?.value())) } } impl ToKeyString for std::ffi::OsString { #[inline] fn fmt_key_string(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(&self.to_string_lossy()) } #[inline] fn with_key_string(&self, f: impl FnOnce(&str) -> R) -> R { f(&self.to_string_lossy()) } } impl_parse_meta_item_primitive!(std::net::IpAddr, syn::LitStr, from_str); impl_parse_meta_item_primitive!(std::net::Ipv4Addr, syn::LitStr, from_str); impl_parse_meta_item_primitive!(std::net::Ipv6Addr, syn::LitStr, from_str); impl_parse_meta_item_primitive!(std::net::SocketAddr, syn::LitStr, from_str); impl_parse_meta_item_primitive!(std::net::SocketAddrV4, syn::LitStr, from_str); impl_parse_meta_item_primitive!(std::net::SocketAddrV6, syn::LitStr, from_str); impl ParseMetaItem for Option { #[inline] fn parse_meta_item(input: ParseStream, mode: ParseMode) -> Result { match mode { ParseMode::Named(_) => T::parse_meta_item(input, mode).map(Some), ParseMode::Unnamed => { mod keywords { syn::custom_keyword!(Some); syn::custom_keyword!(None); } let lookahead = input.lookahead1(); if lookahead.peek(keywords::Some) { input.parse::()?; Paren::parse_delimited_meta_item(input, mode).map(Some) } else if lookahead.peek(keywords::None) { input.parse::()?; Ok(None) } else { Err(lookahead.error()) } } } } #[inline] fn parse_meta_item_flag(span: Span) -> Result { T::parse_meta_item_flag(span).map(Some) } #[inline] fn missing_meta_item(_name: &str, _span: Span) -> Result { Ok(None) } } impl ToKeyString for Option { #[inline] fn fmt_key_string(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Some(v) => { f.write_str("Some(")?; v.fmt_key_string(f)?; f.write_char(')') } None => f.write_str("None"), } } } impl ParseMetaItem for [T; N] { #[inline] fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { Bracket::parse_delimited_meta_item(input, ParseMode::Unnamed) } #[inline] fn parse_meta_item_inline<'s, S: Borrow>>( inputs: &[S], _mode: ParseMode, ) -> Result { Self::parse_meta_flat_unnamed(inputs, _mode, 0) } } impl ParseMetaFlatUnnamed for [T; N] { #[inline] fn field_count() -> Option { Some(N) } fn parse_meta_flat_unnamed<'s, S: Borrow>>( inputs: &[S], _mode: ParseMode, index: usize, ) -> Result { let mut a = arrayvec::ArrayVec::::new(); let errors = Errors::new(); let mut failed = 0; errors.push_result(parse_tuple_struct(inputs, N, |stream, _, _| { match errors.push_result(T::parse_meta_item(stream, ParseMode::Unnamed)) { Some(v) => a.push(v), None => { failed += 1; skip_meta_item(stream); } } Ok(()) })); if a.len() + failed != N { errors.push( inputs_span(inputs), format!( "Expected array at index {} of length {}, got {}", index, N, a.len() + failed, ), ); } errors.check()?; Ok(a.into_inner().unwrap_or_else(|_| unreachable!())) } } impl ToKeyString for [T; N] { fn fmt_key_string(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_char('[')?; for (i, v) in self.iter().enumerate() { if i > 0 { f.write_str(", ")?; } v.fmt_key_string(f)?; } f.write_char(']') } } macro_rules! impl_parse_meta_item_collection { ($ty:ident <$param:ident $(: $bound:tt $(+ $bounds:tt)*)?>, $ident:ident, $item:ident, $push:expr) => { impl<$param: ParseMetaItem $(+ $bound $(+ $bounds)*)?> ParseMetaItem for $ty <$param> { #[inline] fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { Bracket::parse_delimited_meta_item(input, ParseMode::Unnamed) } #[inline] fn parse_meta_item_inline<'s, S: Borrow>>(inputs: &[S], _mode: ParseMode) -> Result { Self::parse_meta_flat_unnamed(inputs, _mode, 0) } } impl<$param: ParseMetaItem $(+ $bound $(+ $bounds)*)?> ParseMetaFlatUnnamed for $ty <$param> { #[inline] fn field_count() -> Option { None } fn parse_meta_flat_unnamed<'s, S: Borrow>>( inputs: &[S], _mode: ParseMode, _index: usize ) -> Result { let mut $ident = Self::new(); let errors = Errors::new(); for input in inputs { let input = input.borrow(); loop { if input.is_empty() { break; } match errors.push_result($param::parse_meta_item(input, ParseMode::Unnamed)) { Some($item) => $push, None => skip_meta_item(input), } if !input.is_empty() { input.parse::()?; } } } errors.check()?; Ok($ident) } } impl<$param: ParseMetaItem $(+ $bound $(+ $bounds)*)?> ParseMetaAppend for $ty <$param> { fn parse_meta_append<'s, S, I, P>(inputs: &[S], paths: I) -> Result where S: Borrow>, I: IntoIterator, I::IntoIter: Clone, P: AsRef { let mut $ident = Self::new(); let errors = Errors::new(); let paths = paths.into_iter(); errors.push_result(parse_struct(inputs, |input, p, pspan| { if paths.clone().any(|path| path.as_ref() == p) { match errors.push_result(<_>::parse_meta_item_named(input, p, pspan)) { Some($item) => $push, None => skip_meta_item(input), } } else { skip_meta_item(input); } Ok(()) })); errors.check()?; Ok($ident) } } impl<$param: ToKeyString $(+ $bound $(+ $bounds)*)?> ToKeyString for $ty <$param> { fn fmt_key_string(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_char('[')?; for (i, v) in self.iter().enumerate() { if i > 0 { f.write_str(", ")?; } v.fmt_key_string(f)?; } f.write_char(']') } } }; } macro_rules! impl_parse_meta_item_set { ($ty:ident <$param:ident $(: $bound:tt $(+ $bounds:tt)*)?>, $ident:ident, $item:ident, $push:expr) => { impl<$param: ParseMetaItem $(+ $bound $(+ $bounds)*)?> ParseMetaItem for $ty <$param> { #[inline] fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { Bracket::parse_delimited_meta_item(input, ParseMode::Unnamed) } #[inline] fn parse_meta_item_inline<'s, S: Borrow>>(inputs: &[S], _mode: ParseMode) -> Result { Self::parse_meta_flat_unnamed(inputs, _mode, 0) } } impl<$param: ParseMetaItem $(+ $bound $(+ $bounds)*)?> ParseMetaFlatUnnamed for $ty <$param> { #[inline] fn field_count() -> Option { None } fn parse_meta_flat_unnamed<'s, S: Borrow>>( inputs: &[S], _mode: ParseMode, _index: usize ) -> Result { let mut $ident = Self::new(); let errors = Errors::new(); for input in inputs { let input = input.borrow(); loop { if input.is_empty() { break; } let span = input.span(); match errors.push_result($param::parse_meta_item(input, ParseMode::Unnamed)) { Some($item) => if !$push { let span = input.span().join(span).unwrap_or(span); errors.push(span, "Duplicate key"); }, None => skip_meta_item(input), } if !input.is_empty() { input.parse::()?; } } } errors.check()?; Ok($ident) } } impl<$param: ParseMetaItem $(+ $bound $(+ $bounds)*)?> ParseMetaAppend for $ty <$param> { fn parse_meta_append<'s, S, I, P>( inputs: &[S], paths: I, ) -> Result where S: Borrow>, I: IntoIterator, I::IntoIter: Clone, P: AsRef { let errors = Errors::new(); let mut $ident = Self::new(); let paths = paths.into_iter(); parse_struct(inputs, |input, p, pspan| { if paths.clone().any(|path| path.as_ref() == p) { let span = input.span(); let $item = <_>::parse_meta_item_named(input, p, pspan); let span = input.span().join(span).unwrap_or(span); match errors.push_result($item) { Some($item) => if !$push { errors.push(span, "Duplicate key"); }, None => skip_meta_item(input), } } else { skip_meta_item(input); } Ok(()) })?; errors.check()?; Ok($ident) } } impl<$param: ToKeyString $(+ $bound $(+ $bounds)*)?> ToKeyString for $ty <$param> { fn fmt_key_string(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_char('[')?; for (i, v) in self.iter().enumerate() { if i > 0 { f.write_str(", ")?; } v.fmt_key_string(f)?; } f.write_char(']') } } }; } macro_rules! impl_parse_meta_item_map { ( $ty:ident <$kp:ident $(: $kbound:tt $(+ $kbounds:tt)*)?, $vp:ident>, $ident:ident, $key:ident, $value:ident, $push:expr ) => { impl<$kp, $vp> ParseMetaItem for $ty <$kp, $vp> where $kp: ParseMetaItem + ToKeyString $(+ $kbound $(+ $kbounds)*)?, $vp: ParseMetaItem, { #[inline] fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { Brace::parse_delimited_meta_item(input, _mode) } #[inline] fn parse_meta_item_inline<'s, S: Borrow>>(inputs: &[S], mode: ParseMode) -> Result { ::parse_meta_flat_unnamed(inputs, mode, 0) } } impl<$kp, $vp> ParseMetaFlatUnnamed for $ty <$kp, $vp> where $kp: ParseMetaItem + ToKeyString $(+ $kbound $(+ $kbounds)*)?, $vp: ParseMetaItem, { #[inline] fn field_count() -> Option { None } fn parse_meta_flat_unnamed<'s, S: Borrow>>( inputs: &[S], _mode: ParseMode, _index: usize ) -> Result { let mut $ident = Self::new(); let errors = Errors::new(); for input in inputs { let input = input.borrow(); loop { if input.is_empty() { break; } let start = input.span(); let $key = $kp::parse_meta_item(input, ParseMode::Unnamed)?; let span = input.span().join(start).unwrap_or(start); let $value = errors.push_result($key.with_key_string(|ks| { <_>::parse_meta_item_named(input, &ks, start) })); match $value { Some($value) => if !$push { errors.push(span, "Duplicate key"); } None => skip_meta_item(input), } if !input.is_empty() { input.parse::()?; } } } errors.check()?; Ok($ident) } } impl<$kp, $vp> ParseMetaRest for $ty <$kp, $vp> where $kp: ParseMetaItem + ToKeyString $(+ $kbound $(+ $kbounds)*)?, $vp: ParseMetaItem, { fn parse_meta_rest<'s, S: Borrow>>( inputs: &[S], exclude: &[&str], ) -> Result { let mut $ident = Self::new(); let errors = Errors::new(); for input in inputs { let input = input.borrow(); loop { if input.is_empty() { break; } let start = input.span(); let $key = $kp::parse_meta_item(input, ParseMode::Unnamed)?; let span = input.span().join(start).unwrap_or(start); let $value = errors.push_result($key.with_key_string(|ks| { if exclude.contains(&ks) { skip_meta_item(input); Ok::<_, crate::Error>(None) } else { Ok(Some(<_>::parse_meta_item_named(input, &ks, start)?)) } })).flatten(); if let Some($value) = $value { if !$push { errors.push(span, "Duplicate key"); } } if !input.is_empty() { input.parse::()?; } } } errors.check()?; Ok($ident) } } impl<$kp, $vp> ToKeyString for $ty <$kp, $vp> where $kp: ToKeyString $(+ $kbound $(+ $kbounds)*)?, $vp: ToKeyString, { fn fmt_key_string(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_char('{')?; for (i, (k, v)) in self.iter().enumerate() { if i > 0 { f.write_str(", ")?; } k.fmt_key_string(f)?; f.write_str(" = ")?; v.fmt_key_string(f)?; } f.write_char('}') } } }; } impl_parse_meta_item_collection!(Vec, v, item, v.push(item)); impl_parse_meta_item_set!(BTreeSet, set, item, set.insert(item)); impl_parse_meta_item_map!(BTreeMap, map, key, value, map.insert(key, value).is_none()); impl_parse_meta_item_collection!(BinaryHeap, heap, item, heap.push(item)); impl_parse_meta_item_set!(HashSet, set, item, set.insert(item)); impl_parse_meta_item_map!(HashMap, map, key, value, map.insert(key, value).is_none()); impl_parse_meta_item_collection!(LinkedList, list, item, list.push_back(item)); impl_parse_meta_item_collection!(VecDeque, v, item, v.push_back(item)); macro_rules! impl_parse_meta_item_wrapper { ($i:ident $(::$ip:ident)* <$param:ident>) => { impl<$param: ParseMetaItem> ParseMetaItem for $i $(::$ip)* <$param> { #[inline] fn parse_meta_item(input: ParseStream, mode: ParseMode) -> Result { Ok(Self::new($param::parse_meta_item(input, mode)?)) } #[inline] fn parse_meta_item_flag(span: Span) -> Result { $param::parse_meta_item_flag(span).map(Self::new) } } }; } impl_parse_meta_item_wrapper!(Box); impl ToKeyString for Box { #[inline] fn fmt_key_string(&self, f: &mut fmt::Formatter) -> fmt::Result { (**self).fmt_key_string(f) } #[inline] fn with_key_string(&self, f: impl FnOnce(&str) -> R) -> R { (**self).with_key_string(f) } } impl_parse_meta_item_wrapper!(std::rc::Rc); impl ToKeyString for std::rc::Rc { #[inline] fn fmt_key_string(&self, f: &mut fmt::Formatter) -> fmt::Result { (**self).fmt_key_string(f) } #[inline] fn with_key_string(&self, f: impl FnOnce(&str) -> R) -> R { (**self).with_key_string(f) } } impl_parse_meta_item_wrapper!(std::cell::Cell); impl ToKeyString for std::cell::Cell { #[inline] fn fmt_key_string(&self, f: &mut fmt::Formatter) -> fmt::Result { self.get().fmt_key_string(f) } #[inline] fn with_key_string(&self, f: impl FnOnce(&str) -> R) -> R { self.get().with_key_string(f) } } impl_parse_meta_item_wrapper!(std::cell::RefCell); impl ToKeyString for std::cell::RefCell { #[inline] fn fmt_key_string(&self, f: &mut fmt::Formatter) -> fmt::Result { self.borrow().fmt_key_string(f) } #[inline] fn with_key_string(&self, f: impl FnOnce(&str) -> R) -> R { self.borrow().with_key_string(f) } } impl<'t, T: ParseMetaItem + Clone> ParseMetaItem for std::borrow::Cow<'t, T> { #[inline] fn parse_meta_item(input: ParseStream, mode: ParseMode) -> Result { Ok(Self::Owned(T::parse_meta_item(input, mode)?)) } #[inline] fn parse_meta_item_flag(span: Span) -> Result { T::parse_meta_item_flag(span).map(Self::Owned) } } impl<'t, T: ToKeyString + Clone> ToKeyString for std::borrow::Cow<'t, T> { #[inline] fn fmt_key_string(&self, f: &mut fmt::Formatter) -> fmt::Result { (**self).fmt_key_string(f) } #[inline] fn with_key_string(&self, f: impl FnOnce(&str) -> R) -> R { (**self).with_key_string(f) } } impl ParseMetaItem for proc_macro2::TokenTree { #[inline] fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { input.step(|cursor| { cursor .token_tree() .ok_or_else(|| crate::Error::new(cursor.span(), "unexpected end of tokens")) }) } } macro_rules! impl_fmt_key_string_display { ($(#[$attr:meta])* $ty:ty) => { $(#[$attr])* impl ToKeyString for $ty { #[inline] fn fmt_key_string(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self, f) } } }; ($ty:ty, #proc_macro) => { impl_fmt_key_string_display!( #[cfg(feature = "proc-macro")] #[cfg_attr(doc_cfg, doc(cfg(feature = "proc-macro")))] $ty ); }; } impl_fmt_key_string_display!(proc_macro2::TokenTree); /// Consumes up to the next comma or the end of the stream. Returns an error if the stream is /// empty or the first token is a comma. impl ParseMetaItem for proc_macro2::TokenStream { #[inline] fn parse_meta_item(input: ParseStream, mode: ParseMode) -> Result { if input.peek(Token![,]) { return Err(syn::Error::new(input.span(), "unexpected comma")); } input.step(|cursor| { let mut cursor = *cursor; let mut tts = Vec::new(); while let Some((tt, rest)) = cursor.token_tree() { match tt { proc_macro2::TokenTree::Punct(p) if p.as_char() == ',' => break, tt => tts.push(tt), } cursor = rest; } if tts.is_empty() { return Err(syn::Error::new(mode.to_span(input), "expected token")); } Ok((tts.into_iter().collect(), cursor)) }) } #[inline] fn parse_meta_item_flag(_: Span) -> Result { Ok(Default::default()) } #[inline] fn missing_meta_item(_name: &str, _span: Span) -> Result { Ok(Default::default()) } } impl ParseMetaFlatUnnamed for proc_macro2::TokenStream { #[inline] fn field_count() -> Option { None } fn parse_meta_flat_unnamed<'s, S: Borrow>>( inputs: &[S], _mode: ParseMode, _index: usize, ) -> Result { let mut tts = Self::new(); for input in inputs { let input = input.borrow(); loop { if input.is_empty() { break; } tts.extend(Self::parse_meta_item(input, ParseMode::Unnamed)?); } } Ok(tts) } } impl_fmt_key_string_display!(proc_macro2::TokenStream); impl ParseMetaItem for proc_macro2::Literal { #[inline] fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { input.step(|cursor| { cursor .literal() .ok_or_else(|| crate::Error::new(cursor.span(), "expected literal")) }) } } impl_fmt_key_string_display!(proc_macro2::Literal); impl ParseMetaItem for proc_macro2::Punct { #[inline] fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { input.step(|cursor| { cursor .punct() .ok_or_else(|| crate::Error::new(cursor.span(), "expected punctuation")) }) } } impl_fmt_key_string_display!(proc_macro2::Punct); impl ParseMetaItem for proc_macro2::Group { fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { input.step(|cursor| { for delim in { use proc_macro2::Delimiter::*; [Parenthesis, Brace, Bracket, None] } { if let Some((group, _, cursor)) = cursor.group(delim) { return Ok((proc_macro2::Group::new(delim, group.token_stream()), cursor)); } } Err(crate::Error::new( cursor.span(), "expected parenthesis or brace or bracket", )) }) } } #[cfg(feature = "proc-macro")] #[inline] fn convert_token_tree(tt: proc_macro2::TokenTree) -> syn::Result { #[inline] fn convert_delimiter(d: proc_macro2::Delimiter) -> proc_macro::Delimiter { match d { proc_macro2::Delimiter::Parenthesis => proc_macro::Delimiter::Parenthesis, proc_macro2::Delimiter::Brace => proc_macro::Delimiter::Brace, proc_macro2::Delimiter::Bracket => proc_macro::Delimiter::Bracket, proc_macro2::Delimiter::None => proc_macro::Delimiter::None, } } #[inline] fn convert_spacing(s: proc_macro2::Spacing) -> proc_macro::Spacing { match s { proc_macro2::Spacing::Alone => proc_macro::Spacing::Alone, proc_macro2::Spacing::Joint => proc_macro::Spacing::Joint, } } Ok(match tt { proc_macro2::TokenTree::Group(g) => { proc_macro::Group::new(convert_delimiter(g.delimiter()), g.stream().into()).into() } proc_macro2::TokenTree::Ident(i) => { proc_macro::Ident::new(&i.to_string(), i.span().unwrap()).into() } proc_macro2::TokenTree::Punct(p) => { proc_macro::Punct::new(p.as_char(), convert_spacing(p.spacing())).into() } proc_macro2::TokenTree::Literal(l) => l .to_string() .parse::() .map(|mut pl| { pl.set_span(l.span().unwrap()); pl }) .map_err(|e| syn::Error::new(l.span(), e))? .into(), }) } #[cfg(feature = "proc-macro")] #[cfg_attr(doc_cfg, doc(cfg(feature = "proc-macro")))] impl ParseMetaItem for proc_macro::TokenTree { #[inline] fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { convert_token_tree(input.parse::()?) } } impl_fmt_key_string_display!(proc_macro::TokenTree, #proc_macro); #[cfg(feature = "proc-macro")] #[cfg_attr(doc_cfg, doc(cfg(feature = "proc-macro")))] impl ParseMetaItem for proc_macro::TokenStream { #[inline] fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { Ok(input.parse::()?.into()) } } impl_fmt_key_string_display!(proc_macro::TokenStream, #proc_macro); #[cfg(feature = "proc-macro")] #[cfg_attr(doc_cfg, doc(cfg(feature = "proc-macro")))] impl ParseMetaItem for proc_macro::Literal { #[inline] fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { match convert_token_tree(proc_macro2::TokenTree::Literal(input.parse()?))? { proc_macro::TokenTree::Literal(l) => Ok(l), _ => unreachable!(), } } } impl_fmt_key_string_display!(proc_macro::Literal, #proc_macro); #[cfg(feature = "proc-macro")] #[cfg_attr(doc_cfg, doc(cfg(feature = "proc-macro")))] impl ParseMetaItem for proc_macro::Punct { #[inline] fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { match convert_token_tree(proc_macro2::TokenTree::Punct(input.parse()?))? { proc_macro::TokenTree::Punct(p) => Ok(p), _ => unreachable!(), } } } impl_fmt_key_string_display!(proc_macro::Punct, #proc_macro); #[cfg(feature = "proc-macro")] #[cfg_attr(doc_cfg, doc(cfg(feature = "proc-macro")))] impl ParseMetaItem for proc_macro::Group { #[inline] fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { match convert_token_tree(proc_macro2::TokenTree::Group(input.parse()?))? { proc_macro::TokenTree::Group(g) => Ok(g), _ => unreachable!(), } } } impl_fmt_key_string_display!(proc_macro::Group, #proc_macro); #[cfg(feature = "proc-macro")] #[cfg_attr(doc_cfg, doc(cfg(feature = "proc-macro")))] impl ParseMetaItem for proc_macro::Ident { #[inline] fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { match convert_token_tree(proc_macro2::TokenTree::Ident(input.parse()?))? { proc_macro::TokenTree::Ident(i) => Ok(i), _ => unreachable!(), } } } impl_fmt_key_string_display!(proc_macro::Ident, #proc_macro); impl ParseMetaItem for Punctuated { #[inline] fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { Bracket::parse_delimited_meta_item(input, ParseMode::Unnamed) } #[inline] fn parse_meta_item_inline<'s, S: Borrow>>( inputs: &[S], _mode: ParseMode, ) -> Result { Self::parse_meta_flat_unnamed(inputs, _mode, 0) } } impl ParseMetaFlatUnnamed for Punctuated { #[inline] fn field_count() -> Option { None } fn parse_meta_flat_unnamed<'s, S: Borrow>>( inputs: &[S], _mode: ParseMode, _index: usize, ) -> Result { let mut p = Punctuated::new(); let errors = Errors::new(); for input in inputs { let input = input.borrow(); loop { if input.is_empty() { break; } match errors.push_result(T::parse_meta_item(input, ParseMode::Unnamed)) { Some(v) => { p.push(v); if !input.is_empty() && errors.push_result(input.parse::

()).is_some() { break; } } None => { // skip tokens until we find a P while !input.is_empty() { if input.peek(P::default()) { input.parse::

()?; break; } input.step(|c| Ok(((), c.token_tree().map(|(_, c)| c).unwrap())))?; } } } } } errors.check()?; Ok(p) } } impl ToKeyString for Punctuated { fn fmt_key_string(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_char('[')?; for p in self.pairs() { match p { syn::punctuated::Pair::Punctuated(t, p) => { t.fmt_key_string(f)?; p.to_token_stream().fmt_key_string(f)?; f.write_char(' ')?; } syn::punctuated::Pair::End(t) => t.fmt_key_string(f)?, } } f.write_char(']') } } macro_rules! impl_parse_meta_item_syn { ($(#[$attr:meta])* $ty:ty) => { $(#[$attr])* impl ParseMetaItem for $ty { #[inline] fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { input.parse() } } $(#[$attr])* impl ToKeyString for $ty { #[inline] fn fmt_key_string(&self, f: &mut fmt::Formatter) -> fmt::Result { self.to_token_stream().fmt_key_string(f) } } }; ($ty:ty, #full) => { impl_parse_meta_item_syn!( #[cfg(feature = "full")] #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] $ty ); }; } macro_rules! impl_parse_meta_paren_item_syn { ($(#[$attr:meta])* $ty:ty) => { $(#[$attr])* impl ParseMetaItem for $ty { #[inline] fn parse_meta_item(input: ParseStream, mode: ParseMode) -> Result { match mode { ParseMode::Named(_) => { let content = Paren::parse_delimited(input)?; let ret = content.parse()?; content.parse::()?; Ok(ret) } ParseMode::Unnamed => Ok(input.parse()?), } } } $(#[$attr])* impl ToKeyString for $ty { #[inline] fn fmt_key_string(&self, f: &mut fmt::Formatter) -> fmt::Result { self.to_token_stream().fmt_key_string(f) } } }; ($ty:ty, #full) => { impl_parse_meta_paren_item_syn!( #[cfg(feature = "full")] #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] $ty ); }; } impl_parse_meta_item_syn!(syn::AngleBracketedGenericArguments); impl_parse_meta_item_syn!(syn::BareFnArg); impl_parse_meta_item_syn!(syn::BoundLifetimes); impl_parse_meta_item_syn!(syn::BinOp); impl_parse_meta_item_syn!(syn::Expr, #full); impl_parse_meta_item_syn!(syn::ExprArray, #full); impl_parse_meta_paren_item_syn!(syn::ExprAssign, #full); impl_parse_meta_item_syn!(syn::ExprCall, #full); impl_parse_meta_item_syn!(syn::ExprCast, #full); impl_parse_meta_item_syn!(syn::ExprField, #full); impl_parse_meta_item_syn!(syn::ExprIndex, #full); impl_parse_meta_item_syn!(syn::ExprLit, #full); impl_parse_meta_item_syn!(syn::ExprMethodCall, #full); impl_parse_meta_item_syn!(syn::ExprParen, #full); impl_parse_meta_item_syn!(syn::ExprPath, #full); impl_parse_meta_item_syn!(syn::ExprRange, #full); impl_parse_meta_item_syn!(syn::ExprRepeat, #full); impl_parse_meta_item_syn!(syn::ExprTuple, #full); impl_parse_meta_item_syn!(syn::FnArg, #full); impl_parse_meta_item_syn!(syn::GenericParam); impl ParseMetaItem for syn::Ident { #[inline] fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { syn::ext::IdentExt::parse_any(input) } } impl ToKeyString for syn::Ident { #[inline] fn fmt_key_string(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self, f) } } impl_parse_meta_item_syn!(syn::Lifetime); impl_parse_meta_item_syn!(syn::LifetimeParam); impl_parse_meta_item_syn!(syn::Lit); impl_parse_meta_item_syn!(syn::LitStr); impl_parse_meta_item_syn!(syn::LitByteStr); impl_parse_meta_item_syn!(syn::LitByte); impl_parse_meta_item_syn!(syn::LitChar); impl_parse_meta_item_syn!(syn::LitInt); impl_parse_meta_item_syn!(syn::LitFloat); impl_parse_meta_item_syn!(syn::LitBool); impl_parse_meta_item_syn!(syn::MetaList); impl_parse_meta_paren_item_syn!(syn::Meta); impl_parse_meta_paren_item_syn!(syn::MetaNameValue); #[cfg(feature = "full")] #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] impl ParseMetaItem for syn::Pat { #[inline] fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { syn::Pat::parse_single(input) } } #[cfg(feature = "full")] #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] impl ToKeyString for syn::Pat { #[inline] fn fmt_key_string(&self, f: &mut fmt::Formatter) -> fmt::Result { self.to_token_stream().fmt_key_string(f) } } impl ParseMetaItem for syn::Path { #[inline] fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { input.parse() } } impl ToKeyString for syn::Path { #[inline] fn fmt_key_string(&self, f: &mut fmt::Formatter) -> fmt::Result { for (i, seg) in self.segments.iter().enumerate() { if i > 0 { f.write_str("::")?; } seg.ident.fmt_key_string(f)?; if !seg.arguments.is_empty() { seg.arguments.to_token_stream().fmt_key_string(f)?; } } Ok(()) } } impl_parse_meta_item_syn!(syn::PathSegment); impl_parse_meta_item_syn!(syn::ParenthesizedGenericArguments); impl_parse_meta_item_syn!(syn::Receiver, #full); impl_parse_meta_item_syn!(syn::Signature, #full); impl_parse_meta_item_syn!(syn::TraitBound); impl_parse_meta_item_syn!(syn::Type); impl_parse_meta_item_syn!(syn::TypeArray); impl_parse_meta_item_syn!(syn::TypeBareFn); impl_parse_meta_item_syn!(syn::TypeImplTrait); impl_parse_meta_item_syn!(syn::TypePath); impl_parse_meta_item_syn!(syn::TypePtr); impl_parse_meta_item_syn!(syn::TypeReference); impl_parse_meta_item_syn!(syn::TypeSlice); impl_parse_meta_item_syn!(syn::TypeTraitObject); impl_parse_meta_item_syn!(syn::TypeTuple); impl_parse_meta_item_syn!(syn::TypeParamBound); impl_parse_meta_item_syn!(syn::UseTree, #full); impl_parse_meta_item_syn!(syn::UnOp); impl_parse_meta_item_syn!(syn::Visibility); impl_parse_meta_item_syn!(syn::WherePredicate); impl ParseMetaItem for () { #[inline] fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { let content = Paren::parse_delimited(input)?; content.parse::()?; Ok(()) } #[inline] fn parse_meta_item_inline<'s, S: Borrow>>( inputs: &[S], _mode: ParseMode, ) -> Result { for input in inputs { input.borrow().parse::()?; } Ok(()) } #[inline] fn parse_meta_item_flag(_: Span) -> Result { Ok(()) } } impl ToKeyString for () { #[inline] fn fmt_key_string(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("()") } #[inline] fn with_key_string(&self, f: impl FnOnce(&str) -> R) -> R { f("()") } } macro_rules! impl_parse_meta_item_tuple { ($len:literal: $($index:tt $param:tt $item:ident)+) => { impl<$($param: ParseMetaItem,)+> ParseMetaItem for ($($param,)+) { #[inline] fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { Paren::parse_delimited_meta_item(input, ParseMode::Unnamed) } #[inline] fn parse_meta_item_inline<'s, S: Borrow>>(inputs: &[S], _mode: ParseMode) -> Result { Self::parse_meta_flat_unnamed(inputs, _mode, 0) } } impl<$($param: ParseMetaItem,)+> ParseMetaFlatUnnamed for ($($param,)+) { #[inline] fn field_count() -> Option { Some($len) } fn parse_meta_flat_unnamed<'s, S: Borrow>>( inputs: &[S], _mode: ParseMode, _index: usize ) -> Result { $(let mut $item = FieldStatus::None;)+ let errors = Errors::new(); errors.push_result(parse_tuple_struct(inputs, $len, |stream, _, index| { match index { $($index => $item.parse_unnamed_item(stream, &errors),)+ _ => unreachable!(), } Ok(()) })); $(if $item.is_none() { errors.push( inputs_span(inputs), format!("Expected tuple of length {}, got {}", $len, $index), ); return errors.bail(); };)+ errors.check()?; $(let $item = $item.unwrap_or_else(|| unreachable!());)+ Ok(($($item,)+)) } } impl<$($param: ToKeyString,)+> ToKeyString for ($($param,)+) { #[inline] fn fmt_key_string(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_char('(')?; $( impl_parse_meta_item_tuple!(@push_comma f, $index); self.$index.fmt_key_string(f)?; )+ f.write_char(')') } } }; (@push_comma $f:ident, 0) => { }; (@push_comma $f:ident, $index:literal) => { $f.write_str(", ")?; }; } impl_parse_meta_item_tuple!(1: 0 T0 t0); impl_parse_meta_item_tuple!(2: 0 T0 t0 1 T1 t1); impl_parse_meta_item_tuple!(3: 0 T0 t0 1 T1 t1 2 T2 t2); impl_parse_meta_item_tuple!(4: 0 T0 t0 1 T1 t1 2 T2 t2 3 T3 t3); impl_parse_meta_item_tuple!(5: 0 T0 t0 1 T1 t1 2 T2 t2 3 T3 t3 4 T4 t4); impl_parse_meta_item_tuple!(6: 0 T0 t0 1 T1 t1 2 T2 t2 3 T3 t3 4 T4 t4 5 T5 t5); impl_parse_meta_item_tuple!(7: 0 T0 t0 1 T1 t1 2 T2 t2 3 T3 t3 4 T4 t4 5 T5 t5 6 T6 t6); impl_parse_meta_item_tuple!(8: 0 T0 t0 1 T1 t1 2 T2 t2 3 T3 t3 4 T4 t4 5 T5 t5 6 T6 t6 7 T7 t7); impl_parse_meta_item_tuple!(9: 0 T0 t0 1 T1 t1 2 T2 t2 3 T3 t3 4 T4 t4 5 T5 t5 6 T6 t6 7 T7 t7 8 T8 t8); impl_parse_meta_item_tuple!(10: 0 T0 t0 1 T1 t1 2 T2 t2 3 T3 t3 4 T4 t4 5 T5 t5 6 T6 t6 7 T7 t7 8 T8 t8 9 T9 t9); impl_parse_meta_item_tuple!(11: 0 T0 t0 1 T1 t1 2 T2 t2 3 T3 t3 4 T4 t4 5 T5 t5 6 T6 t6 7 T7 t7 8 T8 t8 9 T9 t9 10 T10 t10); impl_parse_meta_item_tuple!(12: 0 T0 t0 1 T1 t1 2 T2 t2 3 T3 t3 4 T4 t4 5 T5 t5 6 T6 t6 7 T7 t7 8 T8 t8 9 T9 t9 10 T10 t10 11 T11 t11); impl_parse_meta_item_tuple!(13: 0 T0 t0 1 T1 t1 2 T2 t2 3 T3 t3 4 T4 t4 5 T5 t5 6 T6 t6 7 T7 t7 8 T8 t8 9 T9 t9 10 T10 t10 11 T11 t11 12 T12 t12); impl_parse_meta_item_tuple!(14: 0 T0 t0 1 T1 t1 2 T2 t2 3 T3 t3 4 T4 t4 5 T5 t5 6 T6 t6 7 T7 t7 8 T8 t8 9 T9 t9 10 T10 t10 11 T11 t11 12 T12 t12 13 T13 t13); impl_parse_meta_item_tuple!(15: 0 T0 t0 1 T1 t1 2 T2 t2 3 T3 t3 4 T4 t4 5 T5 t5 6 T6 t6 7 T7 t7 8 T8 t8 9 T9 t9 10 T10 t10 11 T11 t11 12 T12 t12 13 T13 t13 14 T14 t14); impl_parse_meta_item_tuple!(16: 0 T0 t0 1 T1 t1 2 T2 t2 3 T3 t3 4 T4 t4 5 T5 t5 6 T6 t6 7 T7 t7 8 T8 t8 9 T9 t9 10 T10 t10 11 T11 t11 12 T12 t12 13 T13 t13 14 T14 t14 15 T15 t15); deluxe-core-0.5.0/small_string.rs000064400000000000000000000152111046102023000151060ustar 00000000000000/// A string type that can be stored on the stack or on the heap, and can also represent borrowed /// strings. pub struct SmallString<'a>(SmallStringInner<'a>); const MAX_INLINE_LEN: usize = 56; enum SmallStringInner<'a> { Heap(String), Inline(arrayvec::ArrayString), Borrowed(&'a str), } impl<'a> SmallString<'a> { /// Creates a new empty `SmallString`. /// /// Does not allocate. #[inline] #[must_use] pub fn new() -> Self { Self(SmallStringInner::Borrowed("")) } /// Converts this `SmallString` to `SmallString<'static>`. #[inline] #[must_use] pub fn into_owned(self) -> SmallString<'static> { use SmallStringInner::*; SmallString(match self.0 { Heap(s) => Heap(s), Inline(s) => Inline(s), Borrowed(s) => s.try_into().map(Inline).unwrap_or_else(|_| Heap(s.into())), }) } /// Extracts a string slice containing the entire `SmallString`. #[inline] #[must_use] pub fn as_str(&self) -> &str { use SmallStringInner::*; match &self.0 { Heap(s) => s.as_str(), Inline(s) => s.as_str(), Borrowed(s) => s, } } /// Appends the given [`char`] to the end of this `SmallString`. #[inline] pub fn push(&mut self, c: char) { self.push_str(c.encode_utf8(&mut [0; 4])); } /// Appends a given string slice onto the end of this `SmallString`. pub fn push_str(&mut self, s: &str) { use SmallStringInner::*; match &mut self.0 { Heap(h) => h.push_str(s), Inline(i) => { if i.try_push_str(s).is_err() { let mut v = String::from(i.as_str()); v.push_str(s); *self = Self(Heap(v)); } } Borrowed(b) => { if b.len().checked_add(s.len()).expect("integer overflow") > MAX_INLINE_LEN { let mut v = String::from(*b); v.push_str(s); *self = Self(Heap(v)); } else { let mut v = arrayvec::ArrayString::from(b).unwrap(); v.push_str(s); *self = Self(Inline(v)); } } } } } impl<'a> Eq for SmallString<'a> {} impl<'a> PartialEq for SmallString<'a> { #[inline] fn eq(&self, other: &Self) -> bool { self.as_str().eq(other.as_str()) } } impl<'a> PartialEq for SmallString<'a> { #[inline] fn eq(&self, other: &str) -> bool { self.as_str().eq(other) } } impl<'a> PartialEq> for str { #[inline] fn eq(&self, other: &SmallString<'a>) -> bool { self.eq(other.as_str()) } } impl<'a> Ord for SmallString<'a> { #[inline] fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.as_str().cmp(other.as_str()) } } impl<'a> PartialOrd for SmallString<'a> { #[inline] fn partial_cmp(&self, other: &Self) -> Option { self.as_str().partial_cmp(other.as_str()) } } impl<'a> PartialOrd for SmallString<'a> { #[inline] fn partial_cmp(&self, other: &str) -> Option { self.as_str().partial_cmp(other) } } impl<'a> PartialOrd> for str { #[inline] fn partial_cmp(&self, other: &SmallString<'a>) -> Option { self.partial_cmp(other.as_str()) } } impl<'a> std::hash::Hash for SmallString<'a> { #[inline] fn hash(&self, state: &mut H) { self.as_str().hash(state) } } impl<'a> Default for SmallString<'a> { fn default() -> Self { Self::new() } } impl<'a> std::fmt::Debug for SmallString<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use SmallStringInner::*; match &self.0 { Heap(s) => s.fmt(f), Inline(s) => s.fmt(f), Borrowed(s) => s.fmt(f), } } } impl<'a> std::fmt::Display for SmallString<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use SmallStringInner::*; match &self.0 { Heap(s) => s.fmt(f), Inline(s) => s.fmt(f), Borrowed(s) => s.fmt(f), } } } impl<'a> From<&'a str> for SmallString<'a> { #[inline] fn from(value: &'a str) -> Self { Self(SmallStringInner::Borrowed(value)) } } impl<'a> From for SmallString<'a> { #[inline] fn from(value: String) -> Self { if value.len() > MAX_INLINE_LEN { Self(SmallStringInner::Heap(value)) } else { Self(SmallStringInner::Inline( arrayvec::ArrayString::from(value.as_str()).unwrap(), )) } } } impl<'a> From> for SmallString<'a> { #[inline] fn from(value: std::borrow::Cow<'a, str>) -> Self { match value { std::borrow::Cow::Borrowed(b) => b.into(), std::borrow::Cow::Owned(o) => o.into(), } } } impl<'a> std::ops::Deref for SmallString<'a> { type Target = str; #[inline] fn deref(&self) -> &Self::Target { self.as_str() } } impl<'a> AsRef for SmallString<'a> { #[inline] fn as_ref(&self) -> &str { self.as_str() } } impl<'a> AsRef<[u8]> for SmallString<'a> { #[inline] fn as_ref(&self) -> &[u8] { self.as_str().as_bytes() } } impl<'a> std::borrow::Borrow for SmallString<'a> { #[inline] fn borrow(&self) -> &str { self.as_str() } } impl<'a> std::fmt::Write for SmallString<'a> { #[inline] fn write_str(&mut self, s: &str) -> std::fmt::Result { self.push_str(s); Ok(()) } #[inline] fn write_char(&mut self, c: char) -> std::fmt::Result { self.push(c); Ok(()) } } impl<'a> quote::ToTokens for SmallString<'a> { #[inline] fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { self.as_str().to_tokens(tokens); } } impl<'a> crate::ParseMetaItem for SmallString<'a> { #[inline] fn parse_meta_item( input: syn::parse::ParseStream, _mode: crate::ParseMode, ) -> crate::Result { Ok(crate::parse_helpers::key_to_string( &input.parse::()?.token(), )) } } impl<'a> crate::ToKeyString for SmallString<'a> { #[inline] fn fmt_key_string(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { std::fmt::Display::fmt(self, f) } #[inline] fn with_key_string(&self, f: impl FnOnce(&str) -> R) -> R { f(self) } } deluxe-core-0.5.0/util.rs000064400000000000000000000403141046102023000133670ustar 00000000000000use proc_macro2::{Span, TokenStream}; use quote::TokenStreamExt; use std::{ borrow::Borrow, cell::RefCell, fmt::Display, hash::Hash, ops::{Deref, DerefMut}, }; use syn::parse::{ParseBuffer, ParseStream}; use crate::{parse_helpers::inputs_span, parse_meta::*}; /// The error type for parsers. pub type Error = syn::Error; /// The result of a parse method. pub type Result = syn::Result; /// A wrapper for a list of errors. Can be empty. #[derive(Clone, Debug, Default)] #[repr(transparent)] pub struct Errors { // RefCell here so this can be re-entrant when used from parser combinators errors: RefCell>, } impl Errors { #[inline] /// Creates a new empty error list. pub const fn new() -> Self { Self { errors: RefCell::new(None), } } /// Checks if the list contains any errors. #[inline] pub fn is_empty(&self) -> bool { self.errors.borrow().is_none() } /// Reset the list to an empty state. #[inline] pub fn clear(&self) { self.errors.take(); } /// Pushes one error onto the list. This function is a wrapper around [`syn::Error::new`]. #[inline] pub fn push(&self, span: Span, message: T) { self.push_syn(Error::new(span, message)); } /// Pushes one error onto the list, setting the error's span to /// [`Span::call_site()`](proc_macro2::Span::call_site). #[inline] pub fn push_call_site(&self, message: T) { self.push(Span::call_site(), message); } /// Pushes one error onto the list spanning the given syntax tree node. This /// function is a wrapper around [`syn::Error::new_spanned`]. #[inline] pub fn push_spanned(&self, tokens: T, message: U) where T: quote::ToTokens, U: Display, { self.push_syn(Error::new_spanned(tokens, message)); } /// Pushes one previously constructed [`Error`] onto the list. #[inline] pub fn push_syn(&self, error: Error) { let mut storage = self.errors.borrow_mut(); if let Some(storage) = storage.as_mut() { storage.combine(error); } else { storage.replace(error); } } /// Pushes an error onto the list from a [`Result`]. /// /// If `result` is [`Err`], pushes the error and returns [`None`]. If `result` is [`Ok`], /// returns [Some]\(T). #[inline] pub fn push_result(&self, result: Result) -> Option { match result { Ok(t) => Some(t), Err(e) => { self.push_syn(e); None } } } /// Appends all errors from `iter` into this list. #[inline] pub fn extend>(&self, iter: T) { let mut errors = self.errors.borrow_mut(); if let Some(errors) = errors.as_mut() { errors.extend(iter); } else { let mut iter = iter.into_iter(); if let Some(next) = iter.next() { let errors = errors.insert(next); errors.extend(iter); } } } /// Returns `Err` if the list has errors, or `Ok(value)` if the list is empty. /// /// If the list has any errors, returns [`Err`] containing one [`Error`] with all of the errors /// combined using [`Error::combine`](syn::Error::combine). #[inline] pub fn into_result(self, value: T) -> Result { if let Some(err) = self.errors.take() { Err(err) } else { Ok(value) } } /// Checks if the error list is empty. /// /// If the list has any errors, returns [`Err`] containing one [`Error`] with all of the errors /// combined using [`Error::combine`](syn::Error::combine). Otherwise, returns [`Ok`]. #[inline] pub fn check(self) -> Result<()> { self.into_result(()) } /// Returns the inner if the error list has errors. /// /// # Panics /// /// Panics if the error list is empty. #[inline] pub fn unwrap_err(self) -> Error { if let Some(err) = self.errors.take() { err } else { panic!("expected Errors to not be empty"); } } /// Returns a new `Err` if the error list has errors. /// /// # Panics /// /// Panics if the error list is empty. #[inline] pub fn bail(self) -> Result { Err(self.unwrap_err()) } /// Converts the error list into a token stream containing [`std::compile_error`] invocations. /// /// The errors are generated with [`Error::into_compile_error`](syn::Error::into_compile_error). /// /// Returns [`None`] if the list is empty. #[inline] pub fn into_compile_error(self) -> Option { self.errors.take().map(|e| e.into_compile_error()) } /// Returns an iterator of token streams containing [`std::compile_error`] invocations. /// /// Each token stream will contain one invocation. The errors are generated with /// [`Error::into_compile_error`](syn::Error::into_compile_error). #[inline] pub fn into_compile_errors(self) -> impl IntoIterator { self.errors .take() .into_iter() .map(|e| e.into_compile_error()) } /// Creates a token stream containing the current set of errors and `item`. pub fn output_with(self, item: Q) -> TokenStream { let mut tokens = item.into_token_stream(); quote::ToTokens::to_tokens(&self, &mut tokens); tokens } } impl quote::ToTokens for Errors { #[inline] fn to_tokens(&self, tokens: &mut TokenStream) { tokens.extend(self.to_token_stream()); } fn to_token_stream(&self) -> TokenStream { self.errors .borrow() .as_ref() .map(|e| e.to_compile_error()) .unwrap_or_default() } #[inline] fn into_token_stream(self) -> TokenStream where Self: Sized, { self.into_compile_error().unwrap_or_default() } } impl From for Errors { #[inline] fn from(err: Error) -> Self { Self { errors: RefCell::new(Some(err)), } } } impl FromIterator for Errors { #[inline] fn from_iter>(iter: T) -> Self { let mut iter = iter.into_iter(); let errors = iter.next().map(|mut first| { first.extend(iter); first }); Self { errors: RefCell::new(errors), } } } impl IntoIterator for Errors { type Item = Error; type IntoIter = ErrorsIntoIter; #[inline] fn into_iter(self) -> Self::IntoIter { ErrorsIntoIter { errors: self.errors.take().map(|e| e.into_iter()), } } } /// An iterator containing all the errors in an [`Errors`]. pub struct ErrorsIntoIter { errors: Option<::IntoIter>, } impl Iterator for ErrorsIntoIter { type Item = Error; fn next(&mut self) -> Option { self.errors.as_mut().and_then(|e| e.next()) } } /// A wrapper for adding a [`Span`](proc_macro2::Span) to an arbitrary value. /// /// Implementations are provided for all the parsing traits that simply delegate to `T`, capturing /// the inital [`Span`](proc_macro2::Span) from the [`ParseStream`](syn::parse::ParseStream). #[derive(Copy, Clone, Debug)] pub struct SpannedValue { value: T, span: Span, } impl SpannedValue { /// Creates a new value wrapping a T, with the span set to /// [`Span::call_site`](proc_macro2::Span::call_site). #[inline] pub fn new(value: T) -> Self { Self::with_span(value, Span::call_site()) } /// Creates a new value wrapping a T, with the span set to `span`. #[inline] pub fn with_span(value: T, span: Span) -> Self { Self { value, span } } /// Unwraps a `SpannedValue` into a `T`. Note this is an associated function, not a method. #[inline] pub fn into_inner(value: SpannedValue) -> T { value.value } } impl Default for SpannedValue { #[inline] fn default() -> Self { Self::new(T::default()) } } impl Display for SpannedValue { #[inline] fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { self.value.fmt(f) } } impl PartialEq for SpannedValue { #[inline] fn eq(&self, other: &Self) -> bool { self.value == other.value } } impl Eq for SpannedValue {} impl PartialOrd for SpannedValue { #[inline] fn partial_cmp(&self, other: &Self) -> Option { self.value.partial_cmp(&other.value) } } impl Ord for SpannedValue { #[inline] fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.value.cmp(&other.value) } } impl Hash for SpannedValue { #[inline] fn hash(&self, state: &mut H) { self.value.hash(state); } } impl quote::ToTokens for SpannedValue { #[inline] fn to_tokens(&self, tokens: &mut TokenStream) { let mut group = proc_macro2::Group::new(proc_macro2::Delimiter::None, Default::default()); group.set_span(self.span); tokens.append(group); } } impl ParseMetaItem for SpannedValue { #[inline] fn parse_meta_item(input: ParseStream, mode: crate::ParseMode) -> Result { let span = input.span(); let value = T::parse_meta_item(input, mode)?; let span = input.span().join(span).unwrap_or(span); Ok(Self { value, span }) } #[inline] fn parse_meta_item_inline<'s, S: Borrow>>( inputs: &[S], mode: ParseMode, ) -> Result { let span = inputs.first().map(|p| p.borrow().span()); let value = T::parse_meta_item_inline(inputs, mode)?; let span = span .and_then(|s| inputs.last().and_then(|p| p.borrow().span().join(s))) .unwrap_or_else(Span::call_site); Ok(Self { value, span }) } #[inline] fn parse_meta_item_flag(span: Span) -> Result { Ok(Self { value: T::parse_meta_item_flag(span)?, span, }) } #[inline] fn parse_meta_item_named(input: ParseStream, name: &str, span: Span) -> Result { let value = T::parse_meta_item_named(input, name, span)?; let span = input.span().join(span).unwrap_or(span); Ok(Self { value, span }) } } impl ParseMetaFlatUnnamed for SpannedValue { #[inline] fn field_count() -> Option { T::field_count() } fn parse_meta_flat_unnamed<'s, S: Borrow>>( inputs: &[S], mode: ParseMode, index: usize, ) -> Result { let mut span = crate::parse_helpers::inputs_span(inputs); let value = T::parse_meta_flat_unnamed(inputs, mode, index)?; if let Some(closed) = span.join(inputs_span(inputs)) { span = closed; } Ok(Self { value, span }) } } impl ParseMetaFlatNamed for SpannedValue { const ACCEPTS_ALL: bool = T::ACCEPTS_ALL; #[inline] fn field_names() -> &'static [&'static str] { T::field_names() } fn parse_meta_flat_named<'s, S: Borrow>>( inputs: &[S], mode: ParseMode, prefix: &str, validate: bool, ) -> Result { let mut span = crate::parse_helpers::inputs_span(inputs); let value = T::parse_meta_flat_named(inputs, mode, prefix, validate)?; if let Some(closed) = span.join(inputs_span(inputs)) { span = closed; } Ok(Self { value, span }) } } impl ParseMetaAppend for SpannedValue { fn parse_meta_append<'s, S, I, P>(inputs: &[S], paths: I) -> Result where S: Borrow>, I: IntoIterator, I::IntoIter: Clone, P: AsRef, { let mut span = inputs_span(inputs); let value = T::parse_meta_append(inputs, paths)?; if let Some(closed) = span.join(inputs_span(inputs)) { span = closed; } Ok(Self { value, span }) } } impl ParseMetaRest for SpannedValue { fn parse_meta_rest<'s, S: Borrow>>( inputs: &[S], exclude: &[&str], ) -> Result { let mut span = inputs_span(inputs); let value = T::parse_meta_rest(inputs, exclude)?; if let Some(closed) = span.join(inputs_span(inputs)) { span = closed; } Ok(Self { value, span }) } } impl From for SpannedValue { #[inline] fn from(value: T) -> Self { Self { value, span: Span::call_site(), } } } impl Deref for SpannedValue { type Target = T; #[inline] fn deref(&self) -> &Self::Target { &self.value } } impl DerefMut for SpannedValue { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.value } } /// A value for a boolean named field that can only be a name (set) or omitted (unset). /// /// Similar to an [Option]<[SpannedValue]<[bool]>> but does not allow `=` or /// `()` after the field name. Thus, it is only useful with named fields. Parsing this out of a /// tuple struct or tuple variant will always result in a parse error. /// /// It is not necessary to use [`#[deluxe(default)]`](ParseMetaItem#deluxedefault-1) on a field /// using this type. The field will automatically be created with a `false` value if the name is /// omitted. #[derive(Copy, Clone, Debug, Default)] pub struct Flag(Option); impl Flag { /// Creates a new `true` flag value spanned to `span`. #[inline] pub fn set(span: Span) -> Self { Self(Some(span)) } /// Creates a new `true` flag value spanned to [`Span::call_site`]. #[inline] pub fn set_call_site() -> Self { Self(Some(Span::call_site())) } /// Creates a new `false` flag value. #[inline] pub fn unset() -> Self { Self(None) } /// Returns `true` if the flag was set. #[inline] pub fn is_set(&self) -> bool { self.0.is_some() } } impl From for Flag { #[inline] fn from(value: bool) -> Self { Self(value.then(Span::call_site)) } } impl From for bool { #[inline] fn from(value: Flag) -> Self { value.is_set() } } impl Eq for Flag {} impl PartialEq for Flag { #[inline] fn eq(&self, other: &Self) -> bool { self.is_set() == other.is_set() } } impl PartialEq for Flag { #[inline] fn eq(&self, other: &bool) -> bool { self.is_set() == *other } } impl PartialEq for bool { #[inline] fn eq(&self, other: &Flag) -> bool { *self == other.is_set() } } impl quote::ToTokens for Flag { #[inline] fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append(proc_macro2::Ident::new( self.0.map(|_| "true").unwrap_or("false"), self.0.unwrap_or_else(Span::call_site), )); } } impl ParseMetaItem for Flag { #[inline] fn parse_meta_item(input: ParseStream, mode: ParseMode) -> Result { Self::parse_meta_item_inline(&[input], mode) } #[inline] fn parse_meta_item_inline<'s, S: Borrow>>( inputs: &[S], _mode: ParseMode, ) -> Result { Err(Error::new( crate::parse_helpers::inputs_span(inputs), "field with type `Flag` can only be a named field with no value", )) } #[inline] fn parse_meta_item_flag(span: Span) -> Result { Ok(Self(Some(span))) } #[inline] fn parse_meta_item_named(input: ParseStream, _name: &str, span: Span) -> Result { if input.is_empty() || input.peek(syn::Token![,]) { Self::parse_meta_item_flag(span) } else { Err(Error::new(input.span(), "unexpected token")) } } #[inline] fn missing_meta_item(_name: &str, _span: Span) -> Result { Ok(Self::unset()) } } deluxe-core-0.5.0/validations.rs000064400000000000000000000151671046102023000147370ustar 00000000000000//! Additional helper functions for validating after parsing. use crate::Errors; /// Appends an error if more than one given attribute is present. /// /// `attrs` should provide an iterator of tuples containing the field name, and an [`Option`] /// possibly containing the field value. If two or more are [`Some`], an error will be appended to /// `errors`, with `prefix` prepended onto the names. pub fn only_one<'t, I, O>(attrs: I, prefix: &str, errors: &Errors) where I: IntoIterator, I::IntoIter: Clone, O: Into> + Clone + ?Sized + 't, { let iter = attrs.into_iter(); let present_spans = iter.clone().filter_map(|f| f.1.clone().into()); if present_spans.clone().take(2).count() == 2 { let mut names = String::new(); for (n, _) in iter { use std::fmt::Write; let n = crate::parse_helpers::join_path(prefix, n); if names.is_empty() { write!(names, "`{n}`").unwrap(); } else { write!(names, ", `{n}`").unwrap(); } } for span in present_spans { errors.push(span.span(), format!("only one of {names} is allowed")); } } } /// Appends an error if some attributes are present, but not all of them. /// /// `attrs` should provide an iterator of tuples containing the field name, and an [`Option`] /// possibly containing the field value. If at least one is [`Some`] and at least one is [`None`], /// an error will be appended to `errors`, with `prefix` prepended onto the names. pub fn all_or_none<'t, I, O>(attrs: I, prefix: &str, errors: &Errors) where I: IntoIterator, I::IntoIter: Clone, O: Into> + Clone + ?Sized + 't, { let iter = attrs.into_iter(); let mut search = iter.clone().cloned().map(|f| f.1.into()); let first_is_some = search.next().map(|f| f.is_some()).unwrap_or(false); if search.any(|f| f.is_some() != first_is_some) { let mut names = String::new(); for (n, o) in iter.clone().cloned() { if o.into().is_none() { use std::fmt::Write; let n = crate::parse_helpers::join_path(prefix, n); if names.is_empty() { write!(names, "`{n}`").unwrap(); } else { write!(names, ", `{n}`").unwrap(); } } } for (n, o) in iter.cloned() { if let Some(o) = o.into() { errors.push( o.span(), format!("{names} must also be set in order to use `{n}`"), ); } } } } #[macro_export] #[doc(hidden)] macro_rules! _spanned_arg { (($name:expr, $value:expr)) => { &( $name, $value.map(|s| s as &dyn $crate::syn::spanned::Spanned), ) }; ($field:ident) => { &( $crate::stringify!($field), $field .as_ref() .map(|s| s as &dyn $crate::syn::spanned::Spanned), ) }; } /// Appends an error if more than one given attribute is present, automatically casting and /// stringifying field names. /// /// Convenience macro for [`only_one`](fn@only_one). The first argument is a &[str] /// prefix to prepend onto the names in the event of an error. The second argument is an /// &[Errors] to add any errors into. /// /// The rest of the arguments are either identifiers or 2-element tuples. Identifiers must be an /// `Option` and tuples must be `(&str, Option<&T>)`, where `&T` is castable to &dyn /// [syn::spanned::Spanned]. Identifiers will automatically [`stringify`] the identifier /// name to use in the error message, and so should used when the field name is the same as the /// variable name. The tuple form can be used to specify a different field name from the name of /// the variable. The identifier form is intended to make it easier to do validations by /// destructuring a data type after it has been parsed. /// /// # Example /// /// ``` /// #[derive(Default)] /// struct MyStruct { /// my_bool: Option, /// path: Option, /// // #[deluxe(rename = "ty")] /// field3: Option, /// } /// let errors = deluxe_core::Errors::new(); /// let s = MyStruct::default(); /// /// // ... parsing omitted ... /// /// let MyStruct { my_bool, path, field3 } = &s; /// // only one of `my_bool`, `path`, `ty` is allowed /// deluxe_core::only_one!("", &errors, my_bool, path, ("ty", field3.as_ref())); /// # assert!(errors.is_empty()); /// ``` #[macro_export] macro_rules! only_one { ($prefix:expr, $errors:expr $(, $fields:tt)* $(,)?) => { $crate::validations::only_one([$($crate::_spanned_arg!($fields)),*], $prefix, $errors) }; } /// Appends an error if some attributes are present, but not all of them, automatically casting and /// stringifying field names. /// /// Convenience macro for [`all_or_none`](fn@all_or_none). The first argument is a /// &[str] prefix to prepend onto the names in the event of an error. The second /// argument is an &[Errors] to add any errors into. /// /// The rest of the arguments are either identifiers or 2-element tuples. Identifiers must be an /// `Option` and tuples must be `(&str, Option<&T>)`, where `&T` is castable to &dyn /// [syn::spanned::Spanned]. Identifiers will automatically [`stringify`] the identifier /// name to use in the error message, and so should used when the field name is the same as the /// variable name. The tuple form can be used to specify a different field name from the name of /// the variable. The identifier form is intended to make it easier to do validations by /// destructuring a data type after it has been parsed. /// /// # Example /// /// ``` /// #[derive(Default)] /// struct MyStruct { /// my_bool: Option, /// path: Option, /// // #[deluxe(rename = "ty")] /// field3: Option, /// } /// let errors = deluxe_core::Errors::new(); /// let s = MyStruct::default(); /// /// // ... parsing omitted ... /// /// let MyStruct { my_bool, path, field3 } = &s; /// // if any of `my_bool`, `path`, `ty` is present, then all must be present /// deluxe_core::all_or_none!("", &errors, my_bool, path, ("ty", field3.as_ref())); /// # assert!(errors.is_empty()); /// ``` #[macro_export] macro_rules! all_or_none { ($prefix:expr, $errors:expr $(, $fields:tt)* $(,)?) => { $crate::validations::all_or_none([$($crate::_spanned_arg!($fields)),*], $prefix, $errors) }; } deluxe-core-0.5.0/with.rs000064400000000000000000000707111046102023000133710ustar 00000000000000//! Custom parsing helpers for `#[deluxe(with = ...)]`. /// Helpers for parsing any type that implements [`std::str::FromStr`]. /// /// Can be used on a field by specifying the module, like /// `#[deluxe(with = deluxe::with::from_str)]` pub mod from_str { #![allow(missing_docs)] use crate::{Error, ParseMode, Result}; use std::{borrow::Borrow, str::FromStr}; use syn::parse::{ParseBuffer, ParseStream}; #[inline] pub fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result where T::Err: std::fmt::Display, { let s = input.parse::()?; T::from_str(&s.value()).map_err(|e| Error::new_spanned(s, e.to_string())) } #[inline] pub fn parse_meta_item_inline<'s, S: Borrow>, T: FromStr>( inputs: &[S], mode: ParseMode, ) -> Result where T::Err: std::fmt::Display, { crate::parse_helpers::parse_first(inputs, mode, parse_meta_item) } #[inline] pub fn parse_meta_item_flag(span: proc_macro2::Span) -> Result { Err(crate::parse_helpers::flag_disallowed_error(span)) } #[inline] pub fn parse_meta_item_named( input: ParseStream, _name: &str, span: proc_macro2::Span, ) -> Result where T::Err: std::fmt::Display, { crate::parse_named_meta_item_with!(input, span, self) } #[inline] pub fn missing_meta_item(name: &str, span: proc_macro2::Span) -> Result { Err(crate::parse_helpers::missing_field_error(name, span)) } } /// Helpers for parsing any type that implements [`std::str::FromStr`] and [`Default`]. /// /// Can be used on a field by specifying the module, like /// `#[deluxe(with = deluxe::with::from_str_default)]` pub mod from_str_default { #![allow(missing_docs)] use crate::{ParseMode, Result}; use std::{borrow::Borrow, str::FromStr}; use syn::parse::{ParseBuffer, ParseStream}; #[inline] pub fn parse_meta_item(input: ParseStream, mode: ParseMode) -> Result where T::Err: std::fmt::Display, { super::from_str::parse_meta_item(input, mode) } #[inline] pub fn parse_meta_item_inline<'s, S: Borrow>, T: FromStr>( inputs: &[S], mode: ParseMode, ) -> Result where T::Err: std::fmt::Display, { super::from_str::parse_meta_item_inline(inputs, mode) } #[inline] pub fn parse_meta_item_flag(_span: proc_macro2::Span) -> Result { Ok(Default::default()) } pub fn parse_meta_item_named( input: ParseStream, _name: &str, span: proc_macro2::Span, ) -> Result where T::Err: std::fmt::Display, { crate::parse_named_meta_item_with!(input, span, self) } #[inline] pub fn missing_meta_item(_name: &str, _span: proc_macro2::Span) -> Result { Ok(Default::default()) } } /// Helpers for parsing a module path using /// [`syn::Path::parse_mod_style`](::syn::Path::parse_mod_style). /// /// The field should be a `syn::Path`. Can be used on a field by specifying the module, like /// `#[deluxe(with = deluxe::with::mod_path)]` pub mod mod_path { #![allow(missing_docs)] use crate::{ParseMode, Result}; use std::borrow::Borrow; use syn::parse::{ParseBuffer, ParseStream}; #[inline] pub fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { input.call(syn::Path::parse_mod_style) } #[inline] pub fn parse_meta_item_inline<'s, S: Borrow>>( inputs: &[S], mode: ParseMode, ) -> Result { crate::parse_helpers::parse_first(inputs, mode, parse_meta_item) } #[inline] pub fn parse_meta_item_flag(span: proc_macro2::Span) -> Result { Err(crate::parse_helpers::flag_disallowed_error(span)) } #[inline] pub fn parse_meta_item_named( input: ParseStream, _name: &str, span: proc_macro2::Span, ) -> Result { crate::parse_named_meta_item_with!(input, span, self) } #[inline] pub fn missing_meta_item(name: &str, span: proc_macro2::Span) -> Result { Err(crate::parse_helpers::missing_field_error(name, span)) } } /// Helpers for parsing a path allowing any keywords as identifiers, and containing no path arguments. /// /// The field should be a `syn::Path`. Can be used on a field by specifying the module, like /// `#[deluxe(with = deluxe::with::any_path)]` pub mod any_path { #![allow(missing_docs)] use crate::{ParseMode, Result}; use std::borrow::Borrow; use syn::parse::{ParseBuffer, ParseStream}; #[inline] pub fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { input.call(crate::parse_helpers::parse_any_path) } #[inline] pub fn parse_meta_item_inline<'s, S: Borrow>>( inputs: &[S], mode: ParseMode, ) -> Result { crate::parse_helpers::parse_first(inputs, mode, parse_meta_item) } #[inline] pub fn parse_meta_item_flag(span: proc_macro2::Span) -> Result { Err(crate::parse_helpers::flag_disallowed_error(span)) } #[inline] pub fn parse_meta_item_named( input: ParseStream, _name: &str, span: proc_macro2::Span, ) -> Result { crate::parse_named_meta_item_with!(input, span, self) } #[inline] pub fn missing_meta_item(name: &str, span: proc_macro2::Span) -> Result { Err(crate::parse_helpers::missing_field_error(name, span)) } } /// Helpers for parsing any type that implements [`ParseMetaItem`](crate::ParseMetaItem) parsed out /// of a quoted string first. /// /// Can be used on a field by specifying the module, like /// `#[deluxe(with = deluxe::with::quoted)]` pub mod quoted { #![allow(missing_docs)] use crate::{Error, Errors, ParseMetaItem, ParseMode, Result}; use std::borrow::Borrow; use syn::parse::{ParseBuffer, ParseStream}; pub fn parse_meta_item(input: ParseStream, mode: ParseMode) -> Result { let v = input.parse::()?; syn::parse::Parser::parse_str( |stream: ParseStream<'_>| T::parse_meta_item(stream, mode), &v.value(), ) .map_err(|e| { e.into_iter() .map(|e| Error::new(v.span(), e)) .collect::() .check() .unwrap_err() }) } #[inline] pub fn parse_meta_item_inline<'s, S: Borrow>, T: ParseMetaItem>( inputs: &[S], mode: ParseMode, ) -> Result { crate::parse_helpers::parse_first(inputs, mode, parse_meta_item) } #[inline] pub fn parse_meta_item_flag(span: proc_macro2::Span) -> Result { T::parse_meta_item_flag(span) } #[inline] pub fn parse_meta_item_named( input: ParseStream, _name: &str, span: proc_macro2::Span, ) -> Result { crate::parse_named_meta_item_with!(input, span, self) } #[inline] pub fn missing_meta_item(name: &str, span: proc_macro2::Span) -> Result { T::missing_meta_item(name, span) } } /// Helpers for parsing any type that implements [`ParseMetaItem`](crate::ParseMetaItem) possibly /// parsed out of a quoted string first. /// /// Can be used on a field by specifying the module, like /// `#[deluxe(with = deluxe::with::maybe_quoted)]` pub mod maybe_quoted { #![allow(missing_docs)] use crate::{ParseMetaItem, ParseMode, Result}; use std::borrow::Borrow; use syn::parse::{ParseBuffer, ParseStream}; #[inline] pub fn parse_meta_item(input: ParseStream, mode: ParseMode) -> Result { if input.peek(syn::LitStr) { crate::with::quoted::parse_meta_item(input, mode) } else { T::parse_meta_item(input, mode) } } #[inline] pub fn parse_meta_item_inline<'s, S: Borrow>, T: ParseMetaItem>( inputs: &[S], mode: ParseMode, ) -> Result { crate::parse_helpers::parse_first(inputs, mode, parse_meta_item) } #[inline] pub fn parse_meta_item_flag(span: proc_macro2::Span) -> Result { T::parse_meta_item_flag(span) } #[inline] pub fn parse_meta_item_named( input: ParseStream, _name: &str, span: proc_macro2::Span, ) -> Result { crate::parse_named_meta_item_with!(input, span, self) } #[inline] pub fn missing_meta_item(name: &str, span: proc_macro2::Span) -> Result { T::missing_meta_item(name, span) } } /// Helpers for parsing any type that implements [`syn::parse::Parse`](::syn::parse::Parse). /// /// Can be used on a field by specifying the module, like `#[deluxe(with = deluxe::with::syn)]` pub mod syn { #![allow(missing_docs)] use crate::{ParseMode, Result}; use std::borrow::Borrow; use syn::parse::{Parse, ParseBuffer, ParseStream}; #[inline] pub fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { input.parse::() } #[inline] pub fn parse_meta_item_inline<'s, S: Borrow>, T: Parse>( inputs: &[S], mode: ParseMode, ) -> Result { crate::parse_helpers::parse_first(inputs, mode, parse_meta_item) } #[inline] pub fn parse_meta_item_flag(span: proc_macro2::Span) -> Result { crate::parse_helpers::parse_empty(span, T::parse) } #[inline] pub fn parse_meta_item_named( input: ParseStream, _name: &str, span: proc_macro2::Span, ) -> Result { crate::parse_named_meta_item_with!(input, span, self) } #[inline] pub fn missing_meta_item(name: &str, span: proc_macro2::Span) -> Result { Err(crate::parse_helpers::missing_field_error(name, span)) } } /// Helpers for parsing any type that implements [`syn::parse::Parse`](::syn::parse::Parse) parsed /// out of a quoted string first. /// /// Can be used on a field by specifying the module, like /// `#[deluxe(with = deluxe::with::syn_quoted)]` pub mod syn_quoted { #![allow(missing_docs)] use crate::{Error, Errors, ParseMode, Result}; use std::borrow::Borrow; use syn::parse::{Parse, ParseBuffer, ParseStream}; pub fn parse_meta_item(input: ParseStream, _mode: ParseMode) -> Result { let v = input.parse::()?; syn::parse_str(&v.value()).map_err(|e| { e.into_iter() .map(|e| Error::new(v.span(), e)) .collect::() .check() .unwrap_err() }) } #[inline] pub fn parse_meta_item_inline<'s, S: Borrow>, T: Parse>( inputs: &[S], mode: ParseMode, ) -> Result { crate::parse_helpers::parse_first(inputs, mode, parse_meta_item) } #[inline] pub fn parse_meta_item_flag(span: proc_macro2::Span) -> Result { crate::parse_helpers::parse_empty(span, T::parse) } #[inline] pub fn parse_meta_item_named( input: ParseStream, _name: &str, span: proc_macro2::Span, ) -> Result { crate::parse_named_meta_item_with!(input, span, self) } #[inline] pub fn missing_meta_item(name: &str, span: proc_macro2::Span) -> Result { Err(crate::parse_helpers::missing_field_error(name, span)) } } /// Generates a module for parsing an optional value using `#[deluxe(with = ...)]. /// /// Takes three arguments separated by commas: /// - The generated module. Can include attributes and a visibility specifier. /// - The module to use as an inner parser, relative to the new module. /// - The target type. /// /// # Example /// /// Defines a new module named `mod_path_optional` that parses an [`Option`] using /// [`with::mod_path`](self::mod_path). /// /// ``` /// deluxe_core::define_with_optional!( /// pub mod mod_path_optional, /// deluxe_core::with::mod_path, /// syn::Path /// ); /// ``` #[macro_export] macro_rules! define_with_optional { ( $(#[$attrs:meta])* $vis:vis mod $mod:ident, $($path:ident)? $(:: $path_rest:ident)*, $ty:ty $(,)? ) => { $(#[$attrs])* $vis mod $mod { #[repr(transparent)] struct Inner($ty); impl $crate::ParseMetaItem for Inner { #[inline] fn parse_meta_item( input: $crate::syn::parse::ParseStream, mode: $crate::ParseMode, ) -> $crate::Result { $crate::Result::Ok(Self($($path)? $(::$path_rest)* ::parse_meta_item(input, mode)?)) } } #[inline] pub fn parse_meta_item( input: $crate::syn::parse::ParseStream, mode: $crate::ParseMode, ) -> $crate::Result<$crate::Option<$ty>> { $crate::Result::Ok( <$crate::Option as $crate::ParseMetaItem>::parse_meta_item(input, mode)? .map(|p| p.0), ) } #[inline] pub fn parse_meta_item_inline<'s, S: $crate::Borrow<$crate::syn::parse::ParseBuffer<'s>>>( inputs: &[S], mode: $crate::ParseMode, ) -> $crate::Result<$crate::Option<$ty>> { $crate::Result::Ok( <$crate::Option as $crate::ParseMetaItem>::parse_meta_item_inline(inputs, mode)? .map(|p| p.0), ) } #[inline] pub fn parse_meta_item_flag( _span: $crate::Span, ) -> $crate::Result<$crate::Option<$ty>> { $crate::Result::Ok($crate::Option::None) } #[inline] pub fn parse_meta_item_named( input: $crate::syn::parse::ParseStream, _name: &$crate::primitive::str, span: $crate::Span, ) -> $crate::Result<$crate::Option<$ty>> { $crate::parse_named_meta_item_with!(input, span, self) } #[inline] pub fn missing_meta_item( name: &$crate::primitive::str, span: $crate::Span, ) -> $crate::Result<$crate::Option<$ty>> { $crate::Result::Ok($crate::Option::None) } } }; } /// Generates a module for parsing a collection using `#[deluxe(with = ...)]. /// /// Takes three arguments separated by commas: /// - The generated module. Can include attributes and a visibility specifier. /// - The module to use as an inner parser, relative to the new module. /// - The target collection type. /// /// # Example /// /// Defines a new module named `mod_path_vec` that parses a [`Vec`] of paths using /// [`with::mod_path`](self::mod_path). /// /// ``` /// deluxe_core::define_with_collection!( /// pub mod mod_path_vec, /// deluxe_core::with::mod_path, /// Vec /// ); /// ``` #[macro_export] macro_rules! define_with_collection { ( $(#[$attrs:meta])* $vis:vis mod $mod:ident, $($path:ident)? $(:: $path_rest:ident)*, $($coll:ident)? $(:: $colls:ident)* < $ty:ty > $(,)? ) => { $(#[$attrs])* $vis mod $mod { #[repr(transparent)] struct Inner($ty); impl $crate::ParseMetaItem for Inner { #[inline] fn parse_meta_item( input: $crate::syn::parse::ParseStream, mode: $crate::ParseMode, ) -> $crate::Result { $crate::Result::Ok(Self($($path)? $(::$path_rest)* ::parse_meta_item(input, mode)?)) } } #[inline] pub fn parse_meta_item( input: $crate::syn::parse::ParseStream, mode: $crate::ParseMode, ) -> $crate::Result<$($coll)? $(:: $colls)* <$ty>> { $crate::Result::Ok( $crate::Iterator::collect( $crate::Iterator::map( $crate::IntoIterator::into_iter( <$($coll)? $(:: $colls)* as $crate::ParseMetaItem>::parse_meta_item(input, mode)? ), |p| p.0, ), ) ) } #[inline] pub fn parse_meta_item_inline<'s, S: $crate::Borrow<$crate::syn::parse::ParseBuffer<'s>>>( inputs: &[S], mode: $crate::ParseMode, ) -> $crate::Result<$($coll)? $(:: $colls)* <$ty>> { $crate::Result::Ok( $crate::Iterator::collect( $crate::Iterator::map( $crate::IntoIterator::into_iter( <$($coll)? $(:: $colls)* as $crate::ParseMetaItem>::parse_meta_item_inline(inputs, mode)? ), |p| p.0, ), ) ) } #[inline] pub fn parse_meta_item_flag( _span: $crate::Span, ) -> $crate::Result<$($coll)? $(:: $colls)* <$ty>> { $crate::Result::Ok($crate::Default::default()) } #[inline] pub fn parse_meta_item_named( input: $crate::syn::parse::ParseStream, _name: &$crate::primitive::str, span: $crate::Span, ) -> $crate::Result<$($coll)? $(:: $colls)* <$ty>> { $crate::parse_named_meta_item_with!(input, span, self) } #[inline] pub fn missing_meta_item( name: &$crate::primitive::str, span: $crate::Span, ) -> $crate::Result<$($coll)? $(:: $colls)* <$ty>> { $crate::Result::Ok($crate::Default::default()) } #[inline] pub fn field_count() -> $crate::Option<$crate::primitive::usize> { $crate::Option::None } #[inline] pub fn parse_meta_flat_unnamed<'s, S: $crate::Borrow<$crate::syn::parse::ParseBuffer<'s>>>( inputs: &[S], mode: $crate::ParseMode, index: $crate::primitive::usize ) -> $crate::Result<$($coll)? $(:: $colls)* <$ty>> { $crate::Result::Ok( $crate::Iterator::collect( $crate::Iterator::map( $crate::IntoIterator::into_iter( <$($coll)? $(:: $colls)* as $crate::ParseMetaFlatUnnamed>::parse_meta_flat_unnamed(inputs, mode, index)? ), |p| p.0, ), ) ) } #[inline] pub fn parse_meta_append<'s, S, I, P>( inputs: &[S], paths: I, ) -> $crate::Result<$($coll)? $(:: $colls)* <$ty>> where S: $crate::Borrow<$crate::syn::parse::ParseBuffer<'s>>, I: $crate::IntoIterator, I::IntoIter: $crate::Clone, P: $crate::AsRef<$crate::primitive::str>, { $crate::Result::Ok( $crate::Iterator::collect( $crate::Iterator::map( $crate::IntoIterator::into_iter( <$($coll)? $(:: $colls)* as $crate::ParseMetaAppend>::parse_meta_append(inputs, paths)? ), |p| p.0, ), ) ) } } }; } /// Generates a module for parsing a map collection using `#[deluxe(with = ...)]. /// /// Takes four arguments separated by commas: /// - The generated module. Can include attributes and a visibility specifier. /// - The module to use as an inner key parser, relative to the new module. /// - The module to use as an inner value parser, relative to the new module. /// - The target collection type. /// /// # Example /// /// Defines a new module named `mod_path_hashmap` that parses a /// [`HashMap`](std::collections::HashMap) mapping paths to quoted expressions using /// [`with::mod_path`](self::mod_path) for the key. /// /// ``` /// deluxe_core::define_with_map!( /// pub mod mod_path_hashmap, /// deluxe_core::with::mod_path, /// deluxe_core::with::quoted, /// std::collections::HashMap /// ); /// ``` #[macro_export] macro_rules! define_with_map { ( $(#[$attrs:meta])* $vis:vis mod $mod:ident, $($key_path:ident)? $(:: $key_path_rest:ident)*, $($value_path:ident)? $(:: $value_path_rest:ident)*, $($coll:ident)? $(:: $colls:ident)* < $key:ty, $val:ty > $(,)? ) => { $(#[$attrs])* $vis mod $mod { struct InnerKey($key); impl $crate::Eq for InnerKey where $key: $crate::Eq {} impl $crate::PartialEq for InnerKey where $key: $crate::PartialEq { #[inline] fn eq(&self, other: &Self) -> $crate::primitive::bool { self.0.eq(&other.0) } } impl $crate::Hash for InnerKey where $key: $crate::Hash { #[inline] fn hash(&self, state: &mut H) { self.0.hash(state) } } impl $crate::ToKeyString for InnerKey where $key: $crate::ToKeyString { #[inline] fn fmt_key_string(&self, f: &mut $crate::fmt::Formatter) -> $crate::fmt::Result { self.0.fmt_key_string(f) } #[inline] fn with_key_string(&self, f: impl $crate::ops::FnOnce(&$crate::primitive::str) -> R) -> R { self.0.with_key_string(f) } } impl $crate::ParseMetaItem for InnerKey { #[inline] fn parse_meta_item( input: $crate::syn::parse::ParseStream, mode: $crate::ParseMode, ) -> $crate::Result { $crate::Result::Ok(Self( $($key_path)? $(::$key_path_rest)* ::parse_meta_item(input, mode)?, )) } } struct InnerValue($val); impl $crate::ParseMetaItem for InnerValue { #[inline] fn parse_meta_item( input: $crate::syn::parse::ParseStream, mode: $crate::ParseMode, ) -> $crate::Result { $crate::Result::Ok(Self( $($value_path)? $(::$value_path_rest)* ::parse_meta_item(input, mode)?, )) } } #[inline] pub fn parse_meta_item( input: $crate::syn::parse::ParseStream, mode: $crate::ParseMode, ) -> $crate::Result<$($coll)? $(:: $colls)* <$key, $val>> { $crate::Result::Ok( $crate::Iterator::collect( $crate::Iterator::map( $crate::IntoIterator::into_iter( <$crate::HashMap as $crate::ParseMetaItem>::parse_meta_item(input, mode)? ), |(k, v)| (k.0, v.0), ), ) ) } #[inline] pub fn parse_meta_item_inline<'s, S: $crate::Borrow<$crate::syn::parse::ParseBuffer<'s>>>( inputs: &[S], mode: $crate::ParseMode, ) -> $crate::Result<$($coll)? $(:: $colls)* <$key, $val>> { $crate::Result::Ok( $crate::Iterator::collect( $crate::Iterator::map( $crate::IntoIterator::into_iter( <$crate::HashMap as $crate::ParseMetaItem>::parse_meta_item_inline(inputs, mode)? ), |(k, v)| (k.0, v.0), ), ) ) } #[inline] pub fn parse_meta_item_flag( _span: $crate::Span, ) -> $crate::Result<$($coll)? $(:: $colls)* <$key, $val>> { $crate::Result::Ok($crate::Default::default()) } #[inline] pub fn parse_meta_item_named( input: $crate::syn::parse::ParseStream, _name: &$crate::primitive::str, span: $crate::Span, ) -> $crate::Result<$($coll)? $(:: $colls)* <$key, $val>> { $crate::parse_named_meta_item_with!(input, span, self) } #[inline] pub fn missing_meta_item( name: &$crate::primitive::str, span: $crate::Span, ) -> $crate::Result<$($coll)? $(:: $colls)* <$key, $val>> { $crate::Result::Ok($crate::Default::default()) } #[inline] pub fn field_count() -> $crate::Option<$crate::primitive::usize> { $crate::Option::None } #[inline] pub fn parse_meta_flat_unnamed<'s, S: $crate::Borrow<$crate::syn::parse::ParseBuffer<'s>>>( inputs: &[S], mode: $crate::ParseMode, index: $crate::primitive::usize ) -> $crate::Result<$($coll)? $(:: $colls)* <$key, $val>> { $crate::Result::Ok( $crate::Iterator::collect( $crate::Iterator::map( $crate::IntoIterator::into_iter( <$crate::HashMap as $crate::ParseMetaFlatUnnamed>::parse_meta_flat_unnamed(inputs, mode, index)? ), |(k, v)| (k.0, v.0), ), ) ) } #[inline] pub fn parse_meta_rest<'s, S: $crate::Borrow<$crate::syn::parse::ParseBuffer<'s>>>( inputs: &[S], exclude: &[&$crate::primitive::str], ) -> $crate::Result<$($coll)? $(:: $colls)* <$key, $val>> { $crate::Result::Ok( $crate::Iterator::collect( $crate::Iterator::map( $crate::IntoIterator::into_iter( <$crate::HashMap as $crate::ParseMetaRest>::parse_meta_rest(inputs, exclude)? ), |(k, v)| (k.0, v.0), ), ) ) } } }; } /// Helpers for parsing any type that implements [`ParseMetaItem`](crate::ParseMetaItem) as itself. /// /// Only meant to be used from [`define_with_map`](macro@define_with_map). pub mod identity { #![allow(missing_docs)] use crate::{ParseMetaItem, ParseMode, Result}; use std::borrow::Borrow; use syn::parse::{ParseBuffer, ParseStream}; #[inline] pub fn parse_meta_item(input: ParseStream, mode: ParseMode) -> Result { T::parse_meta_item(input, mode) } #[inline] pub fn parse_meta_item_inline<'s, S: Borrow>, T: ParseMetaItem>( inputs: &[S], mode: ParseMode, ) -> Result { T::parse_meta_item_inline(inputs, mode) } #[inline] pub fn parse_meta_item_flag(span: proc_macro2::Span) -> Result { T::parse_meta_item_flag(span) } #[inline] pub fn parse_meta_item_named( input: ParseStream, _name: &str, span: proc_macro2::Span, ) -> Result { crate::parse_named_meta_item_with!(input, span, self) } #[inline] pub fn missing_meta_item(name: &str, span: proc_macro2::Span) -> Result { T::missing_meta_item(name, span) } }