Skip to content

Commit

Permalink
feat!(deriving_via): change syntax
Browse files Browse the repository at this point in the history
(via = <Type>) => (via: <Type>)
  • Loading branch information
mitama committed Mar 11, 2023
1 parent b072813 commit 69be20c
Show file tree
Hide file tree
Showing 35 changed files with 253 additions and 188 deletions.
199 changes: 80 additions & 119 deletions deriving_via/src/impls/deriving_via.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use quote::quote;
use strum::IntoEnumIterator;
use strum_macros::{EnumIter, IntoStaticStr};
use syn::{
Expand All @@ -19,6 +19,7 @@ mod from;
mod from_iterator;
mod from_str;
mod hash;
mod index;
mod into;
mod mul;
mod ord;
Expand All @@ -27,32 +28,6 @@ mod partial_ord;
mod serialize;
mod try_from;

#[derive(Debug, typed_builder::TypedBuilder)]
struct Derive {
path: syn::Path,
#[builder(default, setter(strip_option))]
via: Option<syn::Type>,
}

#[derive(Debug, Default)]
struct DerivingAttributes(Vec<Derive>);

struct Transitive {
#[allow(unused)]
paren_token: syn::token::Paren,
types: Punctuated<syn::Type, syn::Token![->]>,
}

impl Parse for Transitive {
fn parse(input: ParseStream) -> syn::Result<Self> {
let content;
Ok(Transitive {
paren_token: syn::parenthesized!(content in input),
types: content.parse_terminated(syn::Type::parse)?,
})
}
}

#[derive(EnumIter, IntoStaticStr, Clone, Copy)]
#[strum(serialize_all = "PascalCase")]
enum AvailableDerives {
Expand All @@ -73,102 +48,88 @@ enum AvailableDerives {
Arithmetic,
AsRef,
FromIterator,
Index,
}

impl DerivingAttributes {
fn from_attribute(attribute: &syn::Attribute) -> syn::Result<Self> {
#[derive(Debug)]
enum Derive {
Single(Single),
DerivingVia(DerivingVia),
}
#[derive(Debug)]
struct Deriving {
path: syn::Path,
via: Option<Via>,
}

impl FromIterator<Derive> for Vec<crate::impls::deriving_via::Derive> {
fn from_iter<T: IntoIterator<Item = Derive>>(iter: T) -> Self {
type Target = crate::impls::deriving_via::Derive;
iter.into_iter()
.flat_map(|derive| -> Vec<Target> {
match derive {
Derive::Single(Single { path }) => {
vec![Target::builder().path(path).build()]
}
Derive::DerivingVia(DerivingVia {
derive, via: path, ..
}) => {
vec![Target::builder().path(derive).via(path).build()]
}
}
})
.collect()
}
}
impl Parse for Deriving {
fn parse(input: ParseStream) -> syn::Result<Self> {
let path = input.parse()?;

#[derive(Debug)]
struct Single {
path: syn::Path,
let lookahead = input.lookahead1();
if lookahead.peek(syn::token::Paren) {
Ok(Deriving {
path,
via: Some(input.parse()?),
})
} else {
Ok(Deriving { path, via: None })
}
}
}

#[derive(Debug)]
struct DerivingVia {
derive: syn::Path,
via: syn::Type,
}
#[derive(Debug, Clone)]
struct Via {
via: syn::ExprType,
}

impl From<syn::Path> for Single {
fn from(path: syn::Path) -> Self {
Self { path }
}
}
impl From<Via> for syn::Type {
fn from(via: Via) -> Self {
*via.via.ty
}
}

impl From<(syn::Path, syn::Type)> for DerivingVia {
fn from((derive, via): (syn::Path, syn::Type)) -> Self {
Self { derive, via }
}
}
impl Parse for Via {
fn parse(input: ParseStream) -> syn::Result<Self> {
let content;

fn try_parse(input: syn::Expr) -> syn::Result<Derive> {
use syn::Expr::{Assign, Call, Path};
match input {
Path(derive) => AvailableDerives::iter()
.any(|available_derive| derive.path.is_ident(available_derive.into()))
.then(|| Derive::Single(Single::from(derive.path.clone())))
.ok_or_else(|| syn::Error::new_spanned(derive, "unavailable derive")),
Call(syn::ExprCall { func, args, .. }) => match &*func {
Path(path) => match args.iter().collect::<Vec<_>>().as_slice() {
[Assign(syn::ExprAssign { left, right, .. })] => {
if let (Path(keyword), ty) = (&**left, &**right) {
if keyword.path.is_ident("via") {
return if let Ok(ty) = parse2(ty.into_token_stream()) {
Ok(Derive::DerivingVia(DerivingVia::from((
path.path.clone(),
ty,
))))
} else {
Err(syn::Error::new_spanned(ty, "expected: <Type>"))
};
}
}
Err(syn::Error::new_spanned(args, "expected: (via = <Type>)"))
}
_ => Err(syn::Error::new_spanned(args, "expected: (via = <Type>)")),
},
_ => Err(syn::Error::new_spanned(*func, "unavailable custom option")),
},
expr => Err(syn::Error::new_spanned(expr, "expected: (<...>)")),
}
}
let _ = syn::parenthesized!(content in input);

let expr: syn::Expr = parse2(attribute.tokens.to_owned()).unwrap();
use syn::Expr::{Paren, Tuple};
Ok(Self(
match expr {
Paren(expr) => try_parse(*expr.expr).map(|derive| vec![derive]),
Tuple(items) => items.elems.into_iter().map(try_parse).collect(),
expr => Err(syn::Error::new_spanned(expr, "expected: (<Item>, ...)")),
}?
.into_iter()
.collect(),
))
Ok(Via {
via: content.parse()?,
})
}
}

struct DerivingAttributes {
derivings: Punctuated<Deriving, syn::Token![,]>,
}

impl Parse for DerivingAttributes {
fn parse(input: ParseStream) -> syn::Result<Self> {
let content;
let _ = syn::parenthesized!(content in input);

Ok(DerivingAttributes {
derivings: content.parse_terminated(Deriving::parse)?,
})
}
}

struct Transitive {
#[allow(unused)]
paren_token: syn::token::Paren,
types: Punctuated<syn::Type, syn::Token![->]>,
}

impl Parse for Transitive {
fn parse(input: ParseStream) -> syn::Result<Self> {
let content;
Ok(Transitive {
paren_token: syn::parenthesized!(content in input),
types: content.parse_terminated(syn::Type::parse)?,
})
}
}

impl DerivingAttributes {
fn from_attribute(attr: &syn::Attribute) -> syn::Result<Self> {
parse2(attr.tokens.to_owned())
}
}

Expand Down Expand Up @@ -203,7 +164,7 @@ pub(crate) fn impl_deriving_via(input: &syn::DeriveInput) -> TokenStream {

fn extractor(
target: AvailableDerives,
) -> impl FnOnce(&syn::DeriveInput, Option<&syn::Type>) -> TokenStream {
) -> impl FnOnce(&syn::DeriveInput, Option<syn::Type>) -> TokenStream {
use AvailableDerives::*;
match target {
Display => display::extract,
Expand All @@ -223,20 +184,20 @@ fn extractor(
Arithmetic => arithmetic::extract,
AsRef => as_ref::extract,
FromIterator => from_iterator::extract,
Index => index::extract,
}
}

impl DerivingAttributes {
fn into_token_stream(self, input: &syn::DeriveInput) -> TokenStream {
self.0
self.derivings
.into_iter()
.map(|derive| {
AvailableDerives::iter()
.filter_map(|ad| {
derive
.path
.is_ident(ad.into())
.then(|| extractor(ad)(input, derive.via.as_ref()))
derive.path.is_ident(ad.into()).then(|| {
extractor(ad)(input, derive.via.as_ref().cloned().map(Into::into))
})
})
.collect::<Vec<_>>()
.first()
Expand Down
4 changes: 2 additions & 2 deletions deriving_via/src/impls/deriving_via/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use syn::GenericParam;

use crate::utils::extract_fields;

pub(crate) fn extract(input: &syn::DeriveInput, via: Option<&syn::Type>) -> TokenStream {
pub(crate) fn extract(input: &syn::DeriveInput, via: Option<syn::Type>) -> TokenStream {
let struct_name = &input.ident;
let generics = {
let lt = &input.generics.lt_token;
Expand All @@ -27,7 +27,7 @@ pub(crate) fn extract(input: &syn::DeriveInput, via: Option<&syn::Type>) -> Toke
let where_clause = &input.generics.where_clause;
let (accessor, _, constructor) = extract_fields(input);

via.map_or_else(
via.as_ref().map_or_else(
|| {
quote! {
impl #generics std::ops::Add for #struct_name #generic_params #where_clause {
Expand Down
4 changes: 2 additions & 2 deletions deriving_via/src/impls/deriving_via/arithmetic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use proc_macro2::TokenStream;

use crate::impls::deriving_via::{add, mul};

pub(crate) fn extract(input: &syn::DeriveInput, via: Option<&syn::Type>) -> TokenStream {
[add::extract(input, via), mul::extract(input, via)]
pub(crate) fn extract(input: &syn::DeriveInput, via: Option<syn::Type>) -> TokenStream {
[add::extract(input, via.clone()), mul::extract(input, via)]
.into_iter()
.collect()
}
25 changes: 16 additions & 9 deletions deriving_via/src/impls/deriving_via/as_ref.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use itertools::Itertools;
use proc_macro2::TokenStream;
use quote::quote;
use syn::GenericParam;

use crate::utils::extract_fields;

pub(crate) fn extract(input: &syn::DeriveInput, via: Option<&syn::Type>) -> TokenStream {
pub(crate) fn extract(input: &syn::DeriveInput, via: Option<syn::Type>) -> TokenStream {
let struct_name = &input.ident;
let generics = {
let lt = &input.generics.lt_token;
Expand All @@ -13,24 +14,30 @@ pub(crate) fn extract(input: &syn::DeriveInput, via: Option<&syn::Type>) -> Toke

quote! { #lt #params #gt }
};
let generic_params = {
let (generic_params, generic_types) = {
let lt = &input.generics.lt_token;
let params = input.generics.params.iter().filter_map(|p| match p {
GenericParam::Type(ty) => Some(&ty.ident),
_ => None,
});
let params = input
.generics
.params
.iter()
.filter_map(|p| match p {
GenericParam::Type(ty) => Some(&ty.ident),
_ => None,
})
.collect_vec();
let gt = &input.generics.gt_token;

quote! { #lt #(#params),* #gt }
let params = &params[..];
(quote! { #lt #(#params),* #gt }, quote! { #(#params),* })
};
let where_clause = input.generics.where_clause.as_ref();
let predicates = where_clause.map(|wc| &wc.predicates);
let (accessor, field_ty, _) = extract_fields(input);

via.map_or_else(
via.as_ref().map_or_else(
|| {
quote! {
impl<__AsRefT: ?::core::marker::Sized, #generic_params> ::core::convert::AsRef<__AsRefT> for #struct_name #generic_params
impl<__AsRefT: ?::core::marker::Sized, #generic_types> ::core::convert::AsRef<__AsRefT> for #struct_name #generic_params
where
#field_ty: ::core::convert::AsRef<__AsRefT>,
#predicates
Expand Down
4 changes: 2 additions & 2 deletions deriving_via/src/impls/deriving_via/deserialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use syn::GenericParam;

use crate::utils::extract_fields;

pub(crate) fn extract(input: &syn::DeriveInput, via: Option<&syn::Type>) -> TokenStream {
pub(crate) fn extract(input: &syn::DeriveInput, via: Option<syn::Type>) -> TokenStream {
let struct_name = &input.ident;
let _generics = {
let lt = &input.generics.lt_token;
Expand All @@ -31,7 +31,7 @@ pub(crate) fn extract(input: &syn::DeriveInput, via: Option<&syn::Type>) -> Toke
let generics_params = &input.generics.params;
let (_, field_ty, constructor) = extract_fields(input);

via.map_or_else(
via.as_ref().map_or_else(
|| {
quote! {
impl<'de, #generics_params> serde::Deserialize<'de> for #struct_name #generic_params {
Expand Down
4 changes: 2 additions & 2 deletions deriving_via/src/impls/deriving_via/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use syn::GenericParam;

use crate::utils::extract_fields;

pub(crate) fn extract(input: &syn::DeriveInput, via: Option<&syn::Type>) -> TokenStream {
pub(crate) fn extract(input: &syn::DeriveInput, via: Option<syn::Type>) -> TokenStream {
let struct_name = &input.ident;
let generics = {
let lt = &input.generics.lt_token;
Expand All @@ -26,7 +26,7 @@ pub(crate) fn extract(input: &syn::DeriveInput, via: Option<&syn::Type>) -> Toke
let where_clause = &input.generics.where_clause;
let (accessor, ..) = extract_fields(input);

via.map_or_else(
via.as_ref().map_or_else(
|| {
quote! {
impl #generics ::core::fmt::Display for #struct_name #generic_params
Expand Down
2 changes: 1 addition & 1 deletion deriving_via/src/impls/deriving_via/eq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use syn::GenericParam;

use super::partial_eq;

pub(crate) fn extract(input: &syn::DeriveInput, via: Option<&syn::Type>) -> TokenStream {
pub(crate) fn extract(input: &syn::DeriveInput, via: Option<syn::Type>) -> TokenStream {
let struct_name = &input.ident;
let generics = {
let lt = &input.generics.lt_token;
Expand Down
Loading

0 comments on commit 69be20c

Please sign in to comment.