chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

View File

@@ -0,0 +1,62 @@
use quote::ToTokens;
use crate::{Error, FromMeta, Result};
/// Either a path or a closure.
///
/// This type is useful for options that historically took a path,
/// e.g. `#[darling(with = ...)]` or `#[serde(skip_serializing_if = ...)]`
/// and now want to also allow using a closure to avoid needing a separate
/// function declaration.
///
/// In `darling`, this value is wrapped in [`core::convert::identity`] before usage;
/// this allows treatment of the closure and path cases as equivalent, and prevents
/// a closure from accessing locals in the generated code.
#[derive(Debug, Clone)]
pub struct Callable {
/// The callable
call: syn::Expr,
}
impl AsRef<syn::Expr> for Callable {
fn as_ref(&self) -> &syn::Expr {
&self.call
}
}
impl From<syn::ExprPath> for Callable {
fn from(value: syn::ExprPath) -> Self {
Self {
call: syn::Expr::Path(value),
}
}
}
impl From<syn::ExprClosure> for Callable {
fn from(value: syn::ExprClosure) -> Self {
Self {
call: syn::Expr::Closure(value),
}
}
}
impl From<Callable> for syn::Expr {
fn from(value: Callable) -> Self {
value.call
}
}
impl FromMeta for Callable {
fn from_expr(expr: &syn::Expr) -> Result<Self> {
match expr {
syn::Expr::Path(_) | syn::Expr::Closure(_) => Ok(Self { call: expr.clone() }),
_ => Err(Error::unexpected_expr_type(expr)),
}
}
}
impl ToTokens for Callable {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
self.call.to_tokens(tokens);
}
}

99
vendor/darling_core/src/util/flag.rs vendored Normal file
View File

@@ -0,0 +1,99 @@
use proc_macro2::Span;
use syn::{spanned::Spanned, Meta};
use crate::{FromMeta, Result};
/// A meta-item that can be present as a word - with no value - or absent.
///
/// # Defaulting
/// Like `Option`, `Flag` does not require `#[darling(default)]` to be optional.
/// If the caller does not include the property, then an absent `Flag` will be included
/// in the receiver struct.
///
/// # Spans
/// `Flag` keeps the span where its word was seen.
/// This enables attaching custom error messages to the word, such as in the case of two
/// conflicting flags being present.
///
/// # Example
/// ```ignore
/// #[derive(FromMeta)]
/// #[darling(and_then = Self::not_both)]
/// struct Demo {
/// flag_a: Flag,
/// flag_b: Flag,
/// }
///
/// impl Demo {
/// fn not_both(self) -> Result<Self> {
/// if self.flag_a.is_present() && self.flag_b.is_present() {
/// Err(Error::custom("Cannot set flag_a and flag_b").with_span(&self.flag_b.span()))
/// } else {
/// Ok(self)
/// }
/// }
/// }
/// ```
///
/// The above struct would then produce the following error.
///
/// ```ignore
/// #[example(flag_a, flag_b)]
/// // ^^^^^^ Cannot set flag_a and flag_b
/// ```
#[derive(Debug, Clone, Copy, Default)]
pub struct Flag(Option<Span>);
impl Flag {
/// Creates a new `Flag` which corresponds to the presence of a value.
pub fn present() -> Self {
Flag(Some(Span::call_site()))
}
/// Check if the flag is present.
pub fn is_present(&self) -> bool {
self.0.is_some()
}
#[deprecated(since = "0.14.0", note = "Use Flag::is_present")]
pub fn is_some(&self) -> bool {
self.is_present()
}
/// Get the span of the flag, or [`Span::call_site`] if the flag was not present.
pub fn span(&self) -> Span {
self.0.unwrap_or_else(Span::call_site)
}
}
impl FromMeta for Flag {
fn from_none() -> Option<Self> {
Some(Flag(None))
}
fn from_meta(mi: &syn::Meta) -> Result<Self> {
if let Meta::Path(p) = mi {
Ok(Flag(Some(p.span())))
} else {
// The implementation for () will produce an error for all non-path meta items;
// call it to make sure the span behaviors and error messages are the same.
Err(<()>::from_meta(mi).unwrap_err())
}
}
}
impl From<Flag> for bool {
fn from(flag: Flag) -> Self {
flag.is_present()
}
}
impl From<bool> for Flag {
fn from(v: bool) -> Self {
if v {
Flag::present()
} else {
Flag(None)
}
}
}

View File

@@ -0,0 +1,157 @@
use std::fmt;
use std::hash::{Hash, Hasher};
use proc_macro2::{Span, TokenStream};
use quote::ToTokens;
use syn::{Ident, Meta};
use crate::{FromMeta, Result};
/// A wrapper for an `Ident` which also keeps the value as a string.
///
/// This struct can be used to perform string comparisons and operations.
#[derive(Clone, PartialOrd, Ord)]
pub struct IdentString {
ident: Ident,
string: String,
}
impl IdentString {
/// Create a new `IdentString`.
pub fn new(ident: Ident) -> Self {
IdentString {
string: ident.to_string(),
ident,
}
}
/// Get the ident as a `proc_macro2::Ident`.
pub fn as_ident(&self) -> &Ident {
&self.ident
}
/// Get the ident as a string.
pub fn as_str(&self) -> &str {
&self.string
}
/// Get the location of this `Ident` in source.
pub fn span(&self) -> Span {
self.ident.span()
}
/// Apply some transform to the ident's string representation.
///
/// # Panics
/// This will panic if the transform produces an invalid ident.
pub fn map<F, S>(self, map_fn: F) -> Self
where
F: FnOnce(String) -> S,
S: AsRef<str>,
{
let span = self.span();
let string = map_fn(self.string);
Ident::new(string.as_ref(), span).into()
}
}
impl AsRef<Ident> for IdentString {
fn as_ref(&self) -> &Ident {
self.as_ident()
}
}
impl AsRef<str> for IdentString {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl From<Ident> for IdentString {
fn from(ident: Ident) -> Self {
IdentString::new(ident)
}
}
impl From<IdentString> for Ident {
fn from(v: IdentString) -> Ident {
v.ident
}
}
impl From<IdentString> for String {
fn from(v: IdentString) -> String {
v.string
}
}
impl Eq for IdentString {}
impl PartialEq for IdentString {
fn eq(&self, rhs: &Self) -> bool {
self.ident == rhs.ident
}
}
impl PartialEq<String> for IdentString {
fn eq(&self, rhs: &String) -> bool {
self.as_str() == rhs
}
}
impl PartialEq<&str> for IdentString {
fn eq(&self, rhs: &&str) -> bool {
self.as_str() == *rhs
}
}
impl Hash for IdentString {
fn hash<H: Hasher>(&self, state: &mut H) {
self.ident.hash(state);
}
}
impl ToTokens for IdentString {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.ident.to_tokens(tokens);
}
}
impl fmt::Debug for IdentString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.ident)
}
}
impl fmt::Display for IdentString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.ident)
}
}
impl FromMeta for IdentString {
fn from_meta(item: &Meta) -> Result<Self> {
Ident::from_meta(item).map(IdentString::from)
}
}
#[cfg(test)]
mod tests {
use syn::parse_quote;
use super::IdentString;
#[test]
fn convert() {
let i_str = IdentString::new(parse_quote!(t));
assert_eq!(i_str.as_str(), "t");
}
#[test]
fn map_transform() {
let i = IdentString::new(parse_quote!(my));
let after = i.map(|v| format!("var_{}", v));
assert_eq!(after, "var_my");
assert_eq!(after, String::from("var_my"));
}
}

50
vendor/darling_core/src/util/ignored.rs vendored Normal file
View File

@@ -0,0 +1,50 @@
use crate::{
usage::{self, UsesLifetimes, UsesTypeParams},
FromDeriveInput, FromField, FromGenericParam, FromGenerics, FromMeta, FromTypeParam,
FromVariant, Result,
};
/// An efficient way of discarding data from a syntax element.
///
/// All syntax elements will be successfully read into
/// the `Ignored` struct, with all properties discarded.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Ignored;
macro_rules! ignored {
($trayt:ident, $method:ident, $syn:path) => {
impl $trayt for Ignored {
fn $method(_: &$syn) -> Result<Self> {
Ok(Ignored)
}
}
};
}
ignored!(FromGenericParam, from_generic_param, syn::GenericParam);
ignored!(FromGenerics, from_generics, syn::Generics);
ignored!(FromTypeParam, from_type_param, syn::TypeParam);
ignored!(FromMeta, from_meta, syn::Meta);
ignored!(FromDeriveInput, from_derive_input, syn::DeriveInput);
ignored!(FromField, from_field, syn::Field);
ignored!(FromVariant, from_variant, syn::Variant);
impl UsesTypeParams for Ignored {
fn uses_type_params<'a>(
&self,
_opts: &usage::Options,
_: &'a usage::IdentSet,
) -> usage::IdentRefSet<'a> {
Default::default()
}
}
impl UsesLifetimes for Ignored {
fn uses_lifetimes<'a>(
&self,
_opts: &usage::Options,
_: &'a usage::LifetimeSet,
) -> usage::LifetimeRefSet<'a> {
Default::default()
}
}

26
vendor/darling_core/src/util/mod.rs vendored Normal file
View File

@@ -0,0 +1,26 @@
//! Utility types for attribute parsing.
mod callable;
mod flag;
mod ident_string;
mod ignored;
mod over_ride;
mod parse_attribute;
pub mod parse_expr;
mod path_list;
mod path_to_string;
mod shape;
mod spanned_value;
mod with_original;
pub use self::callable::Callable;
pub use self::flag::Flag;
pub use self::ident_string::IdentString;
pub use self::ignored::Ignored;
pub use self::over_ride::Override;
pub use self::parse_attribute::parse_attribute_to_meta_list;
pub use self::path_list::PathList;
pub use self::path_to_string::path_to_string;
pub use self::shape::{AsShape, Shape, ShapeSet};
pub use self::spanned_value::SpannedValue;
pub use self::with_original::WithOriginal;

View File

@@ -0,0 +1,163 @@
use std::fmt;
use syn::Lit;
use crate::ast::NestedMeta;
use crate::{FromMeta, Result};
use self::Override::*;
/// A value which can inherit a default value or have an explicit value specified.
///
/// # Usage
/// This type is meant for attributes like `default` in `darling`, which can take the following forms:
///
/// * `#[darling(default)]`
/// * `#[darling(default="path::to::fn")]`
///
/// In a struct collecting input for this attribute, that would be written as:
///
/// ```rust,ignore
/// use darling::{util::Override, FromField};
/// #[derive(FromField)]
/// #[darling(attributes(darling))]
/// pub struct Options {
/// default: Option<Override<syn::Path>>,
/// }
///
/// impl Options {
/// fn hydrate(self) -> Option<syn::Path> {
/// self.default.map(|ov| ov.unwrap_or(syn::parse_path("::Default::default").unwrap()))
/// }
/// }
/// ```
///
/// The `word` format (with no associated value), would produce `Override::Inherit`, while a list
/// or value format would produce `Override::Explicit`.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Override<T> {
/// Inherit the eventual value from an external source.
Inherit,
/// Explicitly set the value.
Explicit(T),
}
impl<T> Override<T> {
/// Converts from `Override<T>` to `Override<&T>`.
///
/// Produces a new `Override`, containing a reference into the original, leaving the original in place.
pub fn as_ref(&self) -> Override<&T> {
match *self {
Inherit => Inherit,
Explicit(ref val) => Explicit(val),
}
}
/// Converts from `Override<T>` to `Override<&mut T>`.
///
/// Produces a new `Override`, containing a mutable reference into the original.
pub fn as_mut(&mut self) -> Override<&mut T> {
match *self {
Inherit => Inherit,
Explicit(ref mut val) => Explicit(val),
}
}
/// Returns `true` if the override is an `Explicit` value.
pub fn is_explicit(&self) -> bool {
match *self {
Inherit => false,
Explicit(_) => true,
}
}
/// Converts from `Override<T>` to `Option<T>`.
pub fn explicit(self) -> Option<T> {
match self {
Inherit => None,
Explicit(val) => Some(val),
}
}
/// Unwraps an override, yielding the content of an `Explicit`. Otherwise, it returns `optb`.
pub fn unwrap_or(self, optb: T) -> T {
match self {
Inherit => optb,
Explicit(val) => val,
}
}
/// Unwraps an override, yielding the content of an `Explicit`. Otherwise, it calls `op`.
pub fn unwrap_or_else<F>(self, op: F) -> T
where
F: FnOnce() -> T,
{
match self {
Inherit => op(),
Explicit(val) => val,
}
}
}
impl<T: Default> Override<T> {
/// Returns the contained value or the default value of `T`.
pub fn unwrap_or_default(self) -> T {
match self {
Inherit => Default::default(),
Explicit(val) => val,
}
}
}
impl<T> Default for Override<T> {
fn default() -> Self {
Inherit
}
}
impl<T> From<Option<T>> for Override<T> {
fn from(v: Option<T>) -> Self {
match v {
None => Inherit,
Some(val) => Explicit(val),
}
}
}
impl<T: fmt::Display> fmt::Display for Override<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Inherit => write!(f, "Inherit"),
Explicit(ref val) => write!(f, "Explicit `{}`", val),
}
}
}
/// Parses a `Meta`. A bare word will produce `Override::Inherit`, while
/// any value will be forwarded to `T::from_meta`.
impl<T: FromMeta> FromMeta for Override<T> {
fn from_word() -> Result<Self> {
Ok(Inherit)
}
fn from_list(items: &[NestedMeta]) -> Result<Self> {
Ok(Explicit(FromMeta::from_list(items)?))
}
fn from_value(lit: &Lit) -> Result<Self> {
Ok(Explicit(FromMeta::from_value(lit)?))
}
fn from_char(value: char) -> Result<Self> {
Ok(Explicit(FromMeta::from_char(value)?))
}
fn from_string(value: &str) -> Result<Self> {
Ok(Explicit(FromMeta::from_string(value)?))
}
fn from_bool(value: bool) -> Result<Self> {
Ok(Explicit(FromMeta::from_bool(value)?))
}
}

View File

@@ -0,0 +1,86 @@
use crate::{Error, Result};
use std::fmt;
use syn::punctuated::Pair;
use syn::spanned::Spanned;
use syn::{token, Attribute, Meta, MetaList, Path};
/// Try to parse an attribute into a meta list. Path-type meta values are accepted and returned
/// as empty lists with their passed-in path. Name-value meta values and non-meta attributes
/// will cause errors to be returned.
pub fn parse_attribute_to_meta_list(attr: &Attribute) -> Result<MetaList> {
match &attr.meta {
Meta::List(list) => Ok(list.clone()),
Meta::NameValue(nv) => Err(Error::custom(format!(
"Name-value arguments are not supported. Use #[{}(...)]",
DisplayPath(&nv.path)
))
.with_span(&nv)),
Meta::Path(path) => Ok(MetaList {
path: path.clone(),
delimiter: syn::MacroDelimiter::Paren(token::Paren {
span: {
let mut group = proc_macro2::Group::new(
proc_macro2::Delimiter::None,
proc_macro2::TokenStream::new(),
);
group.set_span(attr.span());
group.delim_span()
},
}),
tokens: Default::default(),
}),
}
}
struct DisplayPath<'a>(&'a Path);
impl fmt::Display for DisplayPath<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let path = self.0;
if path.leading_colon.is_some() {
write!(f, "::")?;
}
for segment in path.segments.pairs() {
match segment {
Pair::Punctuated(segment, _) => write!(f, "{}::", segment.ident)?,
Pair::End(segment) => segment.ident.fmt(f)?,
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::parse_attribute_to_meta_list;
use crate::ast::NestedMeta;
use syn::spanned::Spanned;
use syn::{parse_quote, Ident};
#[test]
fn parse_list() {
let meta = parse_attribute_to_meta_list(&parse_quote!(#[bar(baz = 4)])).unwrap();
let nested_meta = NestedMeta::parse_meta_list(meta.tokens).unwrap();
assert_eq!(nested_meta.len(), 1);
}
#[test]
fn parse_path_returns_empty_list() {
let meta = parse_attribute_to_meta_list(&parse_quote!(#[bar])).unwrap();
let nested_meta = NestedMeta::parse_meta_list(meta.tokens).unwrap();
assert!(meta.path.is_ident(&Ident::new("bar", meta.path.span())));
assert!(nested_meta.is_empty());
}
#[test]
fn parse_name_value_returns_error() {
parse_attribute_to_meta_list(&parse_quote!(#[bar = 4])).unwrap_err();
}
#[test]
fn parse_name_value_error_includes_example() {
let err = parse_attribute_to_meta_list(&parse_quote!(#[bar = 4])).unwrap_err();
assert!(err.to_string().contains("#[bar(...)]"));
}
}

View File

@@ -0,0 +1,86 @@
//! Functions to use with `#[darling(with = "...")]` that control how quoted values
//! in [`Meta`] instances are parsed into [`Expr`] fields.
//!
//! Version 1 of syn did not permit expressions on the right-hand side of the `=` in a
//! [`MetaNameValue`](syn::MetaNameValue), so darling accepted string literals and then
//! parsed their contents as expressions.
//! Passing a string literal in this version would have required the use of a raw string
//! to add quotation marks inside the literal.
//!
//! Version 2 of syn removes the requirement that the right-hand side be a literal.
//! For most types, such as [`Path`](syn::Path), the [`FromMeta`] impl can accept the
//! version without quotation marks without causing ambiguity; a path cannot start and
//! end with quotation marks, so removal is automatic.
//!
//! [`Expr`] is the one type where this ambiguity is new and unavoidable. To address this,
//! this module provides different functions for different expected behaviors.
use syn::{Expr, Meta};
use crate::{Error, FromMeta};
/// Parse a [`Meta`] to an [`Expr`]; if the value is a string literal, the emitted
/// expression will be a string literal.
pub fn preserve_str_literal(meta: &Meta) -> crate::Result<Expr> {
match meta {
Meta::Path(_) => Err(Error::unsupported_format("path").with_span(meta)),
Meta::List(_) => Err(Error::unsupported_format("list").with_span(meta)),
Meta::NameValue(nv) => Ok(nv.value.clone()),
}
}
/// Parse a [`Meta`] to an [`Expr`]; if the value is a string literal, the string's
/// contents will be parsed as an expression and emitted.
pub fn parse_str_literal(meta: &Meta) -> crate::Result<Expr> {
match meta {
Meta::Path(_) => Err(Error::unsupported_format("path").with_span(meta)),
Meta::List(_) => Err(Error::unsupported_format("list").with_span(meta)),
Meta::NameValue(nv) => {
if let Expr::Lit(expr_lit) = &nv.value {
Expr::from_value(&expr_lit.lit)
} else {
Ok(nv.value.clone())
}
}
}
}
#[cfg(test)]
mod tests {
use syn::parse_quote;
use super::*;
macro_rules! meta {
($body:expr) => {
{
let attr: ::syn::Attribute = ::syn::parse_quote!(#[ignore = $body]);
attr.meta
}
};
}
#[test]
fn preserve_str() {
assert_eq!(
preserve_str_literal(&meta!("World")).unwrap(),
parse_quote!("World")
);
}
#[test]
fn preserve_binary_exp() {
assert_eq!(
preserve_str_literal(&meta!("World" + 5)).unwrap(),
parse_quote!("World" + 5)
)
}
#[test]
fn parse_ident() {
assert_eq!(
parse_str_literal(&meta!("world")).unwrap(),
parse_quote!(world)
)
}
}

View File

@@ -0,0 +1,105 @@
use std::ops::Deref;
use syn::{Meta, Path};
use crate::ast::NestedMeta;
use crate::{Error, FromMeta, Result};
use super::path_to_string;
/// A list of `syn::Path` instances. This type is used to extract a list of paths from an
/// attribute.
///
/// # Usage
/// An `PathList` field on a struct implementing `FromMeta` will turn `#[builder(derive(serde::Debug, Clone))]` into:
///
/// ```rust,ignore
/// StructOptions {
/// derive: PathList(vec![syn::Path::new("serde::Debug"), syn::Path::new("Clone")])
/// }
/// ```
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct PathList(Vec<Path>);
impl PathList {
/// Create a new list.
pub fn new<T: Into<Path>>(vals: Vec<T>) -> Self {
PathList(vals.into_iter().map(T::into).collect())
}
/// Create a new `Vec` containing the string representation of each path.
pub fn to_strings(&self) -> Vec<String> {
self.0.iter().map(path_to_string).collect()
}
}
impl Deref for PathList {
type Target = Vec<Path>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<Vec<Path>> for PathList {
fn from(v: Vec<Path>) -> Self {
PathList(v)
}
}
impl FromMeta for PathList {
fn from_list(v: &[NestedMeta]) -> Result<Self> {
let mut paths = Vec::with_capacity(v.len());
for nmi in v {
if let NestedMeta::Meta(Meta::Path(ref path)) = *nmi {
paths.push(path.clone());
} else {
return Err(Error::unexpected_type("non-word").with_span(nmi));
}
}
Ok(PathList(paths))
}
}
#[cfg(test)]
mod tests {
use super::PathList;
use crate::FromMeta;
use proc_macro2::TokenStream;
use quote::quote;
use syn::{parse_quote, Attribute, Meta};
/// parse a string as a syn::Meta instance.
fn pm(tokens: TokenStream) -> ::std::result::Result<Meta, String> {
let attribute: Attribute = parse_quote!(#[#tokens]);
Ok(attribute.meta)
}
fn fm<T: FromMeta>(tokens: TokenStream) -> T {
FromMeta::from_meta(&pm(tokens).expect("Tests should pass well-formed input"))
.expect("Tests should pass valid input")
}
#[test]
fn succeeds() {
let paths = fm::<PathList>(quote!(ignore(Debug, Clone, Eq)));
assert_eq!(
paths.to_strings(),
vec![
String::from("Debug"),
String::from("Clone"),
String::from("Eq")
]
);
}
/// Check that the parser rejects non-word members of the list, and that the error
/// has an associated span.
#[test]
fn fails_non_word() {
let input = PathList::from_meta(&pm(quote!(ignore(Debug, Clone = false))).unwrap());
let err = input.unwrap_err();
assert!(err.has_span());
}
}

View File

@@ -0,0 +1,36 @@
/// Transform Rust paths to a readable and comparable string.
///
/// # Limitations
/// * Leading colons are ignored.
/// * Angle brackets and `as` elements are ignored.
///
/// # Example
/// ```rust
/// # use darling_core::util::path_to_string;
/// # use syn::parse_quote;
/// assert_eq!(path_to_string(&parse_quote!(a::b)), "a::b");
/// ```
pub fn path_to_string(path: &syn::Path) -> String {
path.segments
.iter()
.map(|s| s.ident.to_string())
.collect::<Vec<String>>()
.join("::")
}
#[cfg(test)]
mod tests {
use syn::parse_quote;
use super::path_to_string;
#[test]
fn simple_ident() {
assert_eq!(path_to_string(&parse_quote!(a)), "a");
}
#[test]
fn simple_path() {
assert_eq!(path_to_string(&parse_quote!(a::b)), "a::b");
}
}

285
vendor/darling_core/src/util/shape.rs vendored Normal file
View File

@@ -0,0 +1,285 @@
use std::{fmt, iter::FromIterator};
use crate::ast;
/// Get the "shape" of a fields container, such as a struct or variant.
pub trait AsShape {
/// Get the "shape" of a fields container.
fn as_shape(&self) -> Shape;
}
impl<T> AsShape for ast::Fields<T> {
fn as_shape(&self) -> Shape {
match self.style {
ast::Style::Tuple if self.fields.len() == 1 => Shape::Newtype,
ast::Style::Tuple => Shape::Tuple,
ast::Style::Struct => Shape::Named,
ast::Style::Unit => Shape::Unit,
}
}
}
impl AsShape for syn::Fields {
fn as_shape(&self) -> Shape {
match self {
syn::Fields::Named(fields) => fields.as_shape(),
syn::Fields::Unnamed(fields) => fields.as_shape(),
syn::Fields::Unit => Shape::Unit,
}
}
}
impl AsShape for syn::FieldsNamed {
fn as_shape(&self) -> Shape {
Shape::Named
}
}
impl AsShape for syn::FieldsUnnamed {
fn as_shape(&self) -> Shape {
if self.unnamed.len() == 1 {
Shape::Newtype
} else {
Shape::Tuple
}
}
}
impl AsShape for syn::DataStruct {
fn as_shape(&self) -> Shape {
self.fields.as_shape()
}
}
impl AsShape for syn::Variant {
fn as_shape(&self) -> Shape {
self.fields.as_shape()
}
}
/// Description of how fields in a struct or variant are syntactically laid out.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Shape {
/// A set of named fields, e.g. `{ field: String }`.
Named,
/// A list of unnamed fields, e.g. `(String, u64)`.
Tuple,
/// No fields, e.g. `struct Example;`
Unit,
/// A special case of [`Tuple`](Shape#variant.Tuple) with exactly one field, e.g. `(String)`.
Newtype,
}
impl Shape {
pub fn description(&self) -> &'static str {
match self {
Shape::Named => "named fields",
Shape::Tuple => "unnamed fields",
Shape::Unit => "no fields",
Shape::Newtype => "one unnamed field",
}
}
}
impl fmt::Display for Shape {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.description())
}
}
impl AsShape for Shape {
fn as_shape(&self) -> Shape {
*self
}
}
/// A set of [`Shape`] values, which correctly handles the relationship between
/// [newtype](Shape#variant.Newtype) and [tuple](Shape#variant.Tuple) shapes.
///
/// # Example
/// ```rust
/// # use darling_core::util::{Shape, ShapeSet};
/// let shape_set = ShapeSet::new(vec![Shape::Tuple]);
///
/// // This is correct, because all newtypes are single-field tuples.
/// assert!(shape_set.contains(&Shape::Newtype));
/// ```
#[derive(Debug, Clone, Default)]
pub struct ShapeSet {
newtype: bool,
named: bool,
tuple: bool,
unit: bool,
}
impl ShapeSet {
/// Create a new `ShapeSet` which includes the specified items.
///
/// # Exampe
/// ```rust
/// # use darling_core::util::{Shape, ShapeSet};
/// let shape_set = ShapeSet::new(vec![Shape::Named, Shape::Newtype]);
/// assert!(shape_set.contains(&Shape::Newtype));
/// ```
pub fn new(items: impl IntoIterator<Item = Shape>) -> Self {
items.into_iter().collect()
}
/// Insert all possible shapes into the set.
///
/// This is equivalent to calling [`insert`](ShapeSet#method.insert) with every value of [`Shape`].
///
/// # Example
/// ```rust
/// # use darling_core::util::{Shape, ShapeSet};
/// let mut shape_set = ShapeSet::default();
/// shape_set.insert_all();
/// assert!(shape_set.contains(&Shape::Named));
/// ```
pub fn insert_all(&mut self) {
self.insert(Shape::Named);
self.insert(Shape::Newtype);
self.insert(Shape::Tuple);
self.insert(Shape::Unit);
}
/// Insert a shape into the set, so that the set will match that shape
pub fn insert(&mut self, shape: Shape) {
match shape {
Shape::Named => self.named = true,
Shape::Tuple => self.tuple = true,
Shape::Unit => self.unit = true,
Shape::Newtype => self.newtype = true,
}
}
/// Whether this set is empty.
pub fn is_empty(&self) -> bool {
!self.named && !self.newtype && !self.tuple && !self.unit
}
fn contains_shape(&self, shape: Shape) -> bool {
match shape {
Shape::Named => self.named,
Shape::Tuple => self.tuple,
Shape::Unit => self.unit,
Shape::Newtype => self.newtype || self.tuple,
}
}
/// Check if a fields container's shape is in this set.
pub fn contains(&self, fields: &impl AsShape) -> bool {
self.contains_shape(fields.as_shape())
}
/// Check if a field container's shape is in this set of shapes, and produce
/// an [`Error`](crate::Error) if it does not.
pub fn check(&self, fields: &impl AsShape) -> crate::Result<()> {
let shape = fields.as_shape();
if self.contains_shape(shape) {
Ok(())
} else {
Err(crate::Error::unsupported_shape_with_expected(
shape.description(),
self,
))
}
}
fn to_vec(&self) -> Vec<Shape> {
let mut shapes = Vec::with_capacity(3);
if self.named {
shapes.push(Shape::Named);
}
if self.tuple || self.newtype {
shapes.push(if self.tuple {
Shape::Tuple
} else {
Shape::Newtype
});
}
if self.unit {
shapes.push(Shape::Unit)
}
shapes
}
}
impl fmt::Display for ShapeSet {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let shapes = self.to_vec();
match shapes.len() {
0 => write!(f, "nothing"),
1 => write!(f, "{}", shapes[0]),
2 => write!(f, "{} or {}", shapes[0], shapes[1]),
3 => write!(f, "{}, {}, or {}", shapes[0], shapes[1], shapes[2]),
_ => unreachable!(),
}
}
}
impl FromIterator<Shape> for ShapeSet {
fn from_iter<T: IntoIterator<Item = Shape>>(iter: T) -> Self {
let mut output = ShapeSet::default();
for shape in iter.into_iter() {
output.insert(shape);
}
output
}
}
#[cfg(test)]
mod tests {
use syn::parse_quote;
use super::*;
#[test]
fn any_accepts_anything() {
let mut filter = ShapeSet::default();
filter.insert_all();
let unit_struct: syn::DeriveInput = syn::parse_quote! {
struct Example;
};
if let syn::Data::Struct(data) = unit_struct.data {
assert!(filter.contains(&data));
} else {
panic!("Struct not parsed as struct");
};
}
#[test]
fn tuple_accepts_newtype() {
let filter = ShapeSet::new(vec![Shape::Tuple]);
let newtype_struct: syn::DeriveInput = parse_quote! {
struct Example(String);
};
if let syn::Data::Struct(data) = newtype_struct.data {
assert!(filter.contains(&data));
} else {
panic!("Struct not parsed as struct");
};
}
#[test]
fn newtype_rejects_tuple() {
let filter = ShapeSet::new(vec![Shape::Newtype]);
let tuple_struct: syn::DeriveInput = parse_quote! {
struct Example(String, u64);
};
if let syn::Data::Struct(data) = tuple_struct.data {
assert!(!filter.contains(&data));
} else {
panic!("Struct not parsed as struct");
};
}
}

View File

@@ -0,0 +1,145 @@
use proc_macro2::Span;
use std::ops::{Deref, DerefMut};
use syn::spanned::Spanned;
use crate::{
FromDeriveInput, FromField, FromGenericParam, FromGenerics, FromMeta, FromTypeParam,
FromVariant, Result,
};
/// A value and an associated position in source code. The main use case for this is
/// to preserve position information to emit warnings from proc macros. You can use
/// a `SpannedValue<T>` as a field in any struct that implements or derives any of
/// `darling`'s core traits.
///
/// To access the underlying value, use the struct's `Deref` implementation.
///
/// # Defaulting
/// This type is meant to be used in conjunction with attribute-extracted options,
/// but the user may not always explicitly set those options in their source code.
/// In this case, using `Default::default()` will create an instance which points
/// to `Span::call_site()`.
#[derive(Debug, Clone, Copy)]
pub struct SpannedValue<T> {
value: T,
span: Span,
}
impl<T> SpannedValue<T> {
pub fn new(value: T, span: Span) -> Self {
SpannedValue { value, span }
}
/// Get the source code location referenced by this struct.
pub fn span(&self) -> Span {
self.span
}
/// Apply a mapping function to a reference to the spanned value.
pub fn map_ref<U>(&self, map_fn: impl FnOnce(&T) -> U) -> SpannedValue<U> {
SpannedValue::new(map_fn(&self.value), self.span)
}
}
impl<T: Default> Default for SpannedValue<T> {
fn default() -> Self {
SpannedValue::new(Default::default(), Span::call_site())
}
}
impl<T> Deref for SpannedValue<T> {
type Target = T;
fn deref(&self) -> &T {
&self.value
}
}
impl<T> DerefMut for SpannedValue<T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.value
}
}
impl<T> AsRef<T> for SpannedValue<T> {
fn as_ref(&self) -> &T {
&self.value
}
}
macro_rules! spanned {
($trayt:ident, $method:ident, $syn:path) => {
impl<T: $trayt> $trayt for SpannedValue<T> {
fn $method(value: &$syn) -> Result<Self> {
Ok(SpannedValue::new(
$trayt::$method(value).map_err(|e| e.with_span(value))?,
value.span(),
))
}
}
};
}
impl<T: FromMeta> FromMeta for SpannedValue<T> {
fn from_meta(item: &syn::Meta) -> Result<Self> {
let value = T::from_meta(item).map_err(|e| e.with_span(item))?;
let span = match item {
// Example: `#[darling(skip)]` as SpannedValue<bool>
// should have the span pointing to the word `skip`.
syn::Meta::Path(path) => path.span(),
// Example: `#[darling(attributes(Value))]` as a SpannedValue<Vec<String>>
// should have the span pointing to the list contents.
syn::Meta::List(list) => list.tokens.span(),
// Example: `#[darling(skip = true)]` as SpannedValue<bool>
// should have the span pointing to the word `true`.
syn::Meta::NameValue(nv) => nv.value.span(),
};
Ok(Self::new(value, span))
}
fn from_nested_meta(item: &crate::ast::NestedMeta) -> Result<Self> {
T::from_nested_meta(item)
.map(|value| Self::new(value, item.span()))
.map_err(|e| e.with_span(item))
}
fn from_value(literal: &syn::Lit) -> Result<Self> {
T::from_value(literal)
.map(|value| Self::new(value, literal.span()))
.map_err(|e| e.with_span(literal))
}
fn from_expr(expr: &syn::Expr) -> Result<Self> {
T::from_expr(expr)
.map(|value| Self::new(value, expr.span()))
.map_err(|e| e.with_span(expr))
}
}
spanned!(FromGenericParam, from_generic_param, syn::GenericParam);
spanned!(FromGenerics, from_generics, syn::Generics);
spanned!(FromTypeParam, from_type_param, syn::TypeParam);
spanned!(FromDeriveInput, from_derive_input, syn::DeriveInput);
spanned!(FromField, from_field, syn::Field);
spanned!(FromVariant, from_variant, syn::Variant);
impl<T: Spanned> From<T> for SpannedValue<T> {
fn from(value: T) -> Self {
let span = value.span();
SpannedValue::new(value, span)
}
}
#[cfg(test)]
mod tests {
use super::*;
use proc_macro2::Span;
/// Make sure that `SpannedValue` can be seamlessly used as its underlying type.
#[test]
fn deref() {
let test = SpannedValue::new("hello", Span::call_site());
assert_eq!("hello", test.trim());
}
}

View File

@@ -0,0 +1,35 @@
use crate::{
FromDeriveInput, FromField, FromGenericParam, FromGenerics, FromMeta, FromTypeParam,
FromVariant, Result,
};
/// A container to parse some syntax and retain access to the original.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct WithOriginal<T, O> {
pub parsed: T,
pub original: O,
}
impl<T, O> WithOriginal<T, O> {
pub fn new(parsed: T, original: O) -> Self {
WithOriginal { parsed, original }
}
}
macro_rules! with_original {
($trayt:ident, $func:ident, $syn:path) => {
impl<T: $trayt> $trayt for WithOriginal<T, $syn> {
fn $func(value: &$syn) -> Result<Self> {
Ok(WithOriginal::new($trayt::$func(value)?, value.clone()))
}
}
};
}
with_original!(FromDeriveInput, from_derive_input, syn::DeriveInput);
with_original!(FromField, from_field, syn::Field);
with_original!(FromGenerics, from_generics, syn::Generics);
with_original!(FromGenericParam, from_generic_param, syn::GenericParam);
with_original!(FromMeta, from_meta, syn::Meta);
with_original!(FromTypeParam, from_type_param, syn::TypeParam);
with_original!(FromVariant, from_variant, syn::Variant);