rm proc macros
This commit is contained in:
parent
d5ccf62481
commit
2772f2c38b
|
|
@ -1,15 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "e-easy-default"
|
|
||||||
version = "1.0.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
proc-macro-error = { workspace = true }
|
|
||||||
proc-macro2 = { workspace = true }
|
|
||||||
syn = { workspace = true }
|
|
||||||
quote = { workspace = true }
|
|
||||||
|
|
||||||
e-easy-default-core = { path = "./core" }
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
proc-macro = true
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "e-easy-default-core"
|
|
||||||
version = "1.0.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
proc-macro-error = { workspace = true }
|
|
||||||
proc-macro2 = { workspace = true }
|
|
||||||
syn = { workspace = true }
|
|
||||||
quote = { workspace = true }
|
|
||||||
|
|
@ -1,213 +0,0 @@
|
||||||
use proc_macro2::TokenStream;
|
|
||||||
use quote::quote;
|
|
||||||
use syn::{Fields, ItemStruct, Meta};
|
|
||||||
|
|
||||||
pub fn easy_default_core(input: TokenStream) -> TokenStream {
|
|
||||||
let input: ItemStruct = match syn::parse2(input) {
|
|
||||||
Ok(i) => i,
|
|
||||||
Err(e) => return e.to_compile_error(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let struct_name = input.ident;
|
|
||||||
let (ipl, ty, _) = input.generics.split_for_impl();
|
|
||||||
|
|
||||||
match input.fields {
|
|
||||||
Fields::Named(n) => {
|
|
||||||
let mut exprs = Vec::new();
|
|
||||||
|
|
||||||
for field in n.named {
|
|
||||||
let default_expr = field.attrs.iter().find_map(|a| {
|
|
||||||
let default_expr = match &a.meta {
|
|
||||||
Meta::List(list) => match list.path.get_ident() {
|
|
||||||
Some(i) if i == "default" => &list.tokens,
|
|
||||||
_ => return None,
|
|
||||||
},
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
Some(default_expr)
|
|
||||||
});
|
|
||||||
|
|
||||||
let ident = field.ident.expect("named field should have name");
|
|
||||||
match default_expr {
|
|
||||||
Some(expr) => exprs.push(quote! {
|
|
||||||
#ident: #expr,
|
|
||||||
}),
|
|
||||||
None => exprs.push(quote! {
|
|
||||||
#ident: ::core::default::Default::default(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let output = quote! {
|
|
||||||
impl #ipl ::core::default::Default for #struct_name #ty {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
#(#exprs)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
output
|
|
||||||
},
|
|
||||||
Fields::Unnamed(u) => {
|
|
||||||
let mut exprs = Vec::new();
|
|
||||||
|
|
||||||
for field in u.unnamed {
|
|
||||||
let default_expr = field.attrs.iter().find_map(|a| {
|
|
||||||
let default_expr = match &a.meta {
|
|
||||||
Meta::List(list) => match list.path.get_ident() {
|
|
||||||
Some(i) if i == "default" => &list.tokens,
|
|
||||||
_ => return None,
|
|
||||||
},
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
Some(default_expr)
|
|
||||||
});
|
|
||||||
|
|
||||||
match default_expr {
|
|
||||||
Some(expr) => exprs.push(quote! {
|
|
||||||
#expr
|
|
||||||
}),
|
|
||||||
None => exprs.push(quote! {
|
|
||||||
::core::default::Default::default()
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let output = quote! {
|
|
||||||
impl #ipl ::core::default::Default for #struct_name #ty {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self(#(#exprs),*)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
output
|
|
||||||
},
|
|
||||||
Fields::Unit => quote! {
|
|
||||||
impl #ipl ::core::default::Default for #struct_name #ty {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use proc_macro2::TokenStream;
|
|
||||||
use quote::quote;
|
|
||||||
|
|
||||||
use crate::easy_default_core;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn named_fields_struct() {
|
|
||||||
let before = quote! {
|
|
||||||
#[derive(EasyDefault)]
|
|
||||||
struct Test {
|
|
||||||
#[default("Test".to_string())]
|
|
||||||
foo: String,
|
|
||||||
bar: String,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let after = easy_default_core(before);
|
|
||||||
assert_eq!(
|
|
||||||
after.to_string(),
|
|
||||||
quote! {
|
|
||||||
impl ::core::default::Default for Test {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
foo: "Test".to_string(),
|
|
||||||
bar: ::core::default::Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.to_string()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn tuple_struct() {
|
|
||||||
let before = quote! {
|
|
||||||
#[derive(EasyDefault)]
|
|
||||||
struct Test(#[default(5)] u64);
|
|
||||||
};
|
|
||||||
|
|
||||||
let after = easy_default_core(before);
|
|
||||||
assert_eq!(
|
|
||||||
after.to_string(),
|
|
||||||
quote! {
|
|
||||||
impl ::core::default::Default for Test {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self(5)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.to_string()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn unit_struct() {
|
|
||||||
let before = quote! {
|
|
||||||
#[derive(EasyDefault)]
|
|
||||||
struct Test;
|
|
||||||
};
|
|
||||||
|
|
||||||
let after = easy_default_core(before);
|
|
||||||
assert_eq!(
|
|
||||||
after.to_string(),
|
|
||||||
quote! {
|
|
||||||
impl ::core::default::Default for Test {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.to_string(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn assert_output(input: TokenStream, expected: TokenStream) {
|
|
||||||
let after = easy_default_core(input);
|
|
||||||
assert_eq!(after.to_string(), expected.to_string(),)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn other1() {
|
|
||||||
assert_output(
|
|
||||||
quote! {
|
|
||||||
pub struct ObjectMeta {
|
|
||||||
#[default(ObjectKind::Orphan)]
|
|
||||||
pub kind: ObjectKind,
|
|
||||||
#[default(OffsetDateTime::now_utc())]
|
|
||||||
pub created_at: OffsetDateTime,
|
|
||||||
#[default(OffsetDateTime::now_utc())]
|
|
||||||
pub updated_at: OffsetDateTime,
|
|
||||||
pub name: String,
|
|
||||||
pub description: String,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
quote! {
|
|
||||||
impl ::core::default::Default for ObjectMeta {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
kind: ObjectKind::Orphan,
|
|
||||||
created_at: OffsetDateTime::now_utc(),
|
|
||||||
updated_at: OffsetDateTime::now_utc(),
|
|
||||||
name: ::core::default::Default::default(),
|
|
||||||
description: ::core::default::Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
use e_easy_default_core::easy_default_core;
|
|
||||||
use proc_macro::TokenStream;
|
|
||||||
use proc_macro_error::proc_macro_error;
|
|
||||||
|
|
||||||
#[proc_macro_error]
|
|
||||||
#[proc_macro_derive(EasyDefault, attributes(default))]
|
|
||||||
pub fn easy_default(input: TokenStream) -> TokenStream {
|
|
||||||
easy_default_core(input.into()).into()
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "e-easy-from"
|
|
||||||
version = "1.0.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
proc-macro-error = { workspace = true }
|
|
||||||
proc-macro2 = { workspace = true }
|
|
||||||
syn = { workspace = true }
|
|
||||||
quote = { workspace = true }
|
|
||||||
|
|
||||||
e-easy-from-core = { path = "./core" }
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
proc-macro = true
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "e-easy-from-core"
|
|
||||||
version = "1.0.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
proc-macro-error = { workspace = true }
|
|
||||||
proc-macro2 = { workspace = true }
|
|
||||||
syn = { workspace = true }
|
|
||||||
quote = { workspace = true }
|
|
||||||
|
|
@ -1,214 +0,0 @@
|
||||||
use proc_macro2::TokenStream;
|
|
||||||
use quote::{quote, TokenStreamExt};
|
|
||||||
use syn::{spanned::Spanned, Fields, Ident, ItemEnum, ItemStruct, Meta};
|
|
||||||
|
|
||||||
fn handle_enum(e: ItemEnum) -> TokenStream {
|
|
||||||
let name = &e.ident;
|
|
||||||
|
|
||||||
let mut stream: TokenStream = e
|
|
||||||
.variants
|
|
||||||
.iter()
|
|
||||||
.filter_map(|variant| {
|
|
||||||
let variant_name = &variant.ident;
|
|
||||||
|
|
||||||
let has_from_attr = variant.attrs.iter().any(|a| {
|
|
||||||
let path = match &a.meta {
|
|
||||||
Meta::Path(p) => p,
|
|
||||||
_ => return false,
|
|
||||||
};
|
|
||||||
|
|
||||||
path.get_ident().is_some_and(|i| *i == "from")
|
|
||||||
});
|
|
||||||
|
|
||||||
if !has_from_attr {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
match &variant.fields {
|
|
||||||
Fields::Named(_n) => todo!(),
|
|
||||||
Fields::Unnamed(u) => {
|
|
||||||
match u.unnamed.iter().collect::<Vec<_>>().as_slice() {
|
|
||||||
&[] => todo!(),
|
|
||||||
&[field] => {
|
|
||||||
let ty = &field.ty;
|
|
||||||
Some(quote! {
|
|
||||||
impl ::core::convert::From<#ty> for #name {
|
|
||||||
fn from(value: #ty) -> Self {
|
|
||||||
Self::#variant_name(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
_sl => todo!(),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Fields::Unit => todo!(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let has_convert_infallible = e.attrs.iter().any(|a| match &a.meta {
|
|
||||||
Meta::List(list) => {
|
|
||||||
if list.path.get_ident().is_some_and(|i| *i == "from") {
|
|
||||||
let ident: Ident = match syn::parse2(list.tokens.clone()) {
|
|
||||||
Ok(i) => i,
|
|
||||||
_ => return false,
|
|
||||||
};
|
|
||||||
|
|
||||||
ident == "infallible"
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => false,
|
|
||||||
});
|
|
||||||
|
|
||||||
if has_convert_infallible {
|
|
||||||
stream.append_all(quote! {
|
|
||||||
impl ::core::convert::From<::core::convert::Infallible> for #name {
|
|
||||||
fn from(value: ::core::convert::Infallible) -> Self {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
stream
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_struct(s: ItemStruct) -> TokenStream {
|
|
||||||
match &s.fields {
|
|
||||||
Fields::Unnamed(u) => {
|
|
||||||
if u.unnamed.len() != 1 {
|
|
||||||
proc_macro_error::abort!(
|
|
||||||
s.span(),
|
|
||||||
"Tuple struct must have single field"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let field = u.unnamed.first().expect("Should be len 1");
|
|
||||||
let ty = &field.ty;
|
|
||||||
let name = &s.ident;
|
|
||||||
let generics = &s.generics;
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
impl #generics ::core::convert::From<#ty> for #name #generics {
|
|
||||||
fn from(value: #ty) -> Self {
|
|
||||||
Self(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn easy_from_core(input: TokenStream) -> TokenStream {
|
|
||||||
match syn::parse2(input.clone()) {
|
|
||||||
Ok(e) => handle_enum(e),
|
|
||||||
Err(_) => match syn::parse2(input) {
|
|
||||||
Ok(s) => handle_struct(s),
|
|
||||||
Err(e) => e.to_compile_error(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use quote::quote;
|
|
||||||
|
|
||||||
use crate::easy_from_core;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_enum_all_fields_covered() {
|
|
||||||
let input = quote! {
|
|
||||||
#[derive(EasyFrom)]
|
|
||||||
enum Foo {
|
|
||||||
#[from]
|
|
||||||
Bar(u64),
|
|
||||||
|
|
||||||
#[from]
|
|
||||||
Baz(String),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let output = easy_from_core(input).to_string();
|
|
||||||
let expected = quote! {
|
|
||||||
impl ::core::convert::From<u64> for Foo {
|
|
||||||
fn from(value: u64) -> Self {
|
|
||||||
Self::Bar(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::core::convert::From<String> for Foo {
|
|
||||||
fn from(value: String) -> Self {
|
|
||||||
Self::Baz(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
assert_eq!(output, expected)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_enum_some_fields_covered() {
|
|
||||||
let input = quote! {
|
|
||||||
#[derive(EasyFrom)]
|
|
||||||
enum Foo {
|
|
||||||
#[from]
|
|
||||||
Bar(u64),
|
|
||||||
Baz(String),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let output = easy_from_core(input).to_string();
|
|
||||||
let expected = quote! {
|
|
||||||
impl ::core::convert::From<u64> for Foo {
|
|
||||||
fn from(value: u64) -> Self {
|
|
||||||
Self::Bar(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
assert_eq!(output, expected)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_enum_from_infallible() {
|
|
||||||
let input = quote! {
|
|
||||||
#[derive(EasyFrom)]
|
|
||||||
#[from(infallible)]
|
|
||||||
enum Never {}
|
|
||||||
};
|
|
||||||
let output = easy_from_core(input).to_string();
|
|
||||||
let expected = quote! {
|
|
||||||
impl ::core::convert::From<::core::convert::Infallible> for Never {
|
|
||||||
fn from(value: ::core::convert::Infallible) -> Self {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
assert_eq!(output, expected)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_newtype_struct() {
|
|
||||||
let input = quote! {
|
|
||||||
#[derive(EasyFrom)]
|
|
||||||
struct NewType<T>(#[from] T);
|
|
||||||
};
|
|
||||||
let output = easy_from_core(input).to_string();
|
|
||||||
let expected = quote! {
|
|
||||||
impl<T> ::core::convert::From<T> for NewType<T> {
|
|
||||||
fn from(value: T) -> Self {
|
|
||||||
Self(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
assert_eq!(output, expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
use e_easy_from_core::easy_from_core;
|
|
||||||
use proc_macro::TokenStream;
|
|
||||||
use proc_macro_error::proc_macro_error;
|
|
||||||
|
|
||||||
#[proc_macro_error]
|
|
||||||
#[proc_macro_derive(EasyFrom, attributes(from))]
|
|
||||||
pub fn easy_from(input: TokenStream) -> TokenStream {
|
|
||||||
easy_from_core(input.into()).into()
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "e-easy-get"
|
|
||||||
version = "1.0.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
proc-macro-error = { workspace = true }
|
|
||||||
proc-macro2 = { workspace = true }
|
|
||||||
syn = { workspace = true }
|
|
||||||
quote = { workspace = true }
|
|
||||||
|
|
||||||
e-easy-get-core = { path = "./core" }
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
proc-macro = true
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "e-easy-get-core"
|
|
||||||
version = "1.0.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
proc-macro-error = { workspace = true }
|
|
||||||
proc-macro2 = { workspace = true }
|
|
||||||
syn = { workspace = true }
|
|
||||||
quote = { workspace = true }
|
|
||||||
|
|
||||||
e-easy-default = { path = "../../easy-default" }
|
|
||||||
|
|
@ -1,280 +0,0 @@
|
||||||
use proc_macro2::TokenStream;
|
|
||||||
use quote::{format_ident, quote, ToTokens, TokenStreamExt};
|
|
||||||
use syn::{
|
|
||||||
Attribute, Field, Fields, FieldsNamed, Generics, Ident, ItemStruct, LitStr,
|
|
||||||
Meta,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
enum Visibility {
|
|
||||||
#[default]
|
|
||||||
Inherited,
|
|
||||||
Pub,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToTokens for Visibility {
|
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
||||||
match self {
|
|
||||||
Visibility::Pub => tokens.append_all(quote! { pub }),
|
|
||||||
Visibility::Inherited => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct GetOptions {
|
|
||||||
visibility: Visibility,
|
|
||||||
rename: Option<String>,
|
|
||||||
}
|
|
||||||
impl GetOptions {
|
|
||||||
fn from_attr(attr: &Attribute) -> syn::Result<Self> {
|
|
||||||
let mut out = Self::default();
|
|
||||||
attr.parse_nested_meta(|nm| {
|
|
||||||
if nm.path.is_ident("pub") {
|
|
||||||
out.visibility = Visibility::Pub;
|
|
||||||
}
|
|
||||||
|
|
||||||
if nm.path.is_ident("rename") {
|
|
||||||
let name = nm.value()?;
|
|
||||||
let name: LitStr = name.parse()?;
|
|
||||||
|
|
||||||
out.rename = Some(name.value());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn named_fields(
|
|
||||||
name: Ident,
|
|
||||||
generics: Generics,
|
|
||||||
n: FieldsNamed,
|
|
||||||
) -> TokenStream {
|
|
||||||
let (ipl, ty, _) = generics.split_for_impl();
|
|
||||||
|
|
||||||
n.named
|
|
||||||
.into_iter()
|
|
||||||
.flat_map(|field| {
|
|
||||||
let field_name =
|
|
||||||
field.ident.as_ref().expect("named should have ident");
|
|
||||||
let field_ty = &field.ty;
|
|
||||||
|
|
||||||
fn options_for(
|
|
||||||
attr_name: &str,
|
|
||||||
field: &Field,
|
|
||||||
) -> Option<syn::Result<GetOptions>> {
|
|
||||||
field.attrs.iter().find_map(|a| match &a.meta {
|
|
||||||
Meta::List(list) => list
|
|
||||||
.path
|
|
||||||
.get_ident()
|
|
||||||
.filter(|i| *i == attr_name)
|
|
||||||
.map(|_| GetOptions::from_attr(a)),
|
|
||||||
Meta::Path(p) => p
|
|
||||||
.get_ident()
|
|
||||||
.filter(|i| *i == attr_name)
|
|
||||||
.map(|_| Ok(GetOptions::default())),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let get = options_for("get", &field);
|
|
||||||
|
|
||||||
fn output_for(
|
|
||||||
options: Option<syn::Result<GetOptions>>,
|
|
||||||
field_name: &Ident,
|
|
||||||
f: impl FnOnce(Ident, Visibility) -> TokenStream,
|
|
||||||
) -> Option<TokenStream> {
|
|
||||||
match options {
|
|
||||||
Some(Ok(get)) => {
|
|
||||||
let GetOptions {
|
|
||||||
visibility, rename, ..
|
|
||||||
} = get;
|
|
||||||
|
|
||||||
let fn_name = if let Some(rename) = rename {
|
|
||||||
format_ident!("{}", rename)
|
|
||||||
} else {
|
|
||||||
field_name.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
Some(f(fn_name, visibility))
|
|
||||||
},
|
|
||||||
Some(Err(e)) => Some(e.into_compile_error()),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let get_out = output_for(get, field_name, |fn_name, vis| {
|
|
||||||
quote! {
|
|
||||||
impl #ipl #name #ty {
|
|
||||||
#vis fn #fn_name(&self) -> #field_ty {
|
|
||||||
self.#field_name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let get_ref = options_for("get_ref", &field);
|
|
||||||
|
|
||||||
let get_ref_out =
|
|
||||||
output_for(get_ref, field_name, |fn_name, vis| {
|
|
||||||
quote! {
|
|
||||||
impl #ipl #name #ty {
|
|
||||||
#vis fn #fn_name(&self) -> &#field_ty {
|
|
||||||
&self.#field_name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let get_mut = options_for("get_mut", &field);
|
|
||||||
let get_mut_renamed = get_mut
|
|
||||||
.as_ref()
|
|
||||||
.is_some_and(|r| r.as_ref().is_ok_and(|o| o.rename.is_some()));
|
|
||||||
let get_mut_out =
|
|
||||||
output_for(get_mut, field_name, |fn_name, vis| {
|
|
||||||
let fn_name = if get_mut_renamed {
|
|
||||||
fn_name
|
|
||||||
} else {
|
|
||||||
format_ident!("{}_mut", fn_name)
|
|
||||||
};
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
impl #ipl #name #ty {
|
|
||||||
#vis fn #fn_name(&mut self) -> &mut #field_ty {
|
|
||||||
&mut self.#field_name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
get_out.into_iter().chain(get_ref_out).chain(get_mut_out)
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn e_easy_get_core(input: TokenStream) -> TokenStream {
|
|
||||||
let item: ItemStruct = match syn::parse2(input) {
|
|
||||||
Ok(i) => i,
|
|
||||||
Err(e) => return e.into_compile_error(),
|
|
||||||
};
|
|
||||||
|
|
||||||
match item.fields {
|
|
||||||
Fields::Named(n) => named_fields(item.ident, item.generics, n),
|
|
||||||
_ => todo!("aaaaaa"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use proc_macro2::TokenStream;
|
|
||||||
use quote::quote;
|
|
||||||
|
|
||||||
use crate::e_easy_get_core;
|
|
||||||
|
|
||||||
fn expect_output(input: TokenStream, output: TokenStream) {
|
|
||||||
assert_eq!(e_easy_get_core(input).to_string(), output.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_get_pub() {
|
|
||||||
expect_output(
|
|
||||||
quote! {
|
|
||||||
#[derive(EasyGet)]
|
|
||||||
pub struct Foo {
|
|
||||||
#[get(pub)]
|
|
||||||
bar: usize,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
quote! {
|
|
||||||
impl Foo {
|
|
||||||
pub fn bar(&self) -> usize {
|
|
||||||
self.bar
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_get_generic() {
|
|
||||||
expect_output(
|
|
||||||
quote! {
|
|
||||||
#[derive(EasyGet)]
|
|
||||||
pub struct Foo<T> {
|
|
||||||
#[get(pub)]
|
|
||||||
bar: T,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
quote! {
|
|
||||||
impl<T> Foo<T> {
|
|
||||||
pub fn bar(&self) -> T {
|
|
||||||
self.bar
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_get_ref_generic_default() {
|
|
||||||
expect_output(
|
|
||||||
quote! {
|
|
||||||
#[derive(EasyGet)]
|
|
||||||
pub struct Foo<T = usize> {
|
|
||||||
#[get_ref(pub)]
|
|
||||||
bar: T,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
quote! {
|
|
||||||
impl<T> Foo<T> {
|
|
||||||
pub fn bar(&self) -> &T {
|
|
||||||
&self.bar
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rename() {
|
|
||||||
expect_output(
|
|
||||||
quote! {
|
|
||||||
#[derive(EasyGet)]
|
|
||||||
pub struct Foo<T = usize> {
|
|
||||||
#[get_ref(pub, rename = "baz")]
|
|
||||||
bar: T,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
quote! {
|
|
||||||
impl<T> Foo<T> {
|
|
||||||
pub fn baz(&self) -> &T {
|
|
||||||
&self.bar
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_get_mut() {
|
|
||||||
expect_output(
|
|
||||||
quote! {
|
|
||||||
#[derive(EasyGet)]
|
|
||||||
pub struct Foo<T = usize> {
|
|
||||||
#[get_mut(pub)]
|
|
||||||
bar: T,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
quote! {
|
|
||||||
impl<T> Foo<T> {
|
|
||||||
pub fn bar_mut(&mut self) -> &mut T {
|
|
||||||
&mut self.bar
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
use e_easy_get_core::e_easy_get_core;
|
|
||||||
use proc_macro::TokenStream;
|
|
||||||
use proc_macro_error::proc_macro_error;
|
|
||||||
|
|
||||||
#[proc_macro_error]
|
|
||||||
#[proc_macro_derive(EasyGet, attributes(get, get_ref, get_mut))]
|
|
||||||
pub fn easy_get(input: TokenStream) -> TokenStream {
|
|
||||||
e_easy_get_core(input.into()).into()
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "e-impl-for-refs"
|
|
||||||
version = "1.0.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
proc-macro-error = { workspace = true }
|
|
||||||
proc-macro2 = { workspace = true }
|
|
||||||
syn = { workspace = true }
|
|
||||||
quote = { workspace = true }
|
|
||||||
|
|
||||||
e-impl-for-refs-core = { path = "./core" }
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
proc-macro = true
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "e-impl-for-refs-core"
|
|
||||||
version = "1.0.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
proc-macro-error = { workspace = true }
|
|
||||||
proc-macro2 = { workspace = true }
|
|
||||||
syn = { workspace = true }
|
|
||||||
quote = { workspace = true }
|
|
||||||
|
|
@ -1,108 +0,0 @@
|
||||||
use proc_macro2::TokenStream;
|
|
||||||
use quote::quote;
|
|
||||||
use syn::{FnArg, ItemTrait, Pat};
|
|
||||||
|
|
||||||
pub fn impl_for_refs_core(
|
|
||||||
_attrs: TokenStream,
|
|
||||||
item: TokenStream,
|
|
||||||
) -> TokenStream {
|
|
||||||
let trt: ItemTrait = match syn::parse2(item.clone()) {
|
|
||||||
Ok(e) => e,
|
|
||||||
Err(e) => return e.into_compile_error(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let trt_name = &trt.ident;
|
|
||||||
|
|
||||||
let fns = trt
|
|
||||||
.items
|
|
||||||
.iter()
|
|
||||||
.filter_map(|i| match i {
|
|
||||||
syn::TraitItem::Fn(f) => {
|
|
||||||
if f.default.is_some() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let sig = &f.sig;
|
|
||||||
let fn_name = &f.sig.ident;
|
|
||||||
let args = sig.inputs.iter().map(|i| match i {
|
|
||||||
FnArg::Receiver(_s) => {
|
|
||||||
quote!((&**self))
|
|
||||||
},
|
|
||||||
FnArg::Typed(t) => match t.pat.as_ref() {
|
|
||||||
Pat::Ident(e) => quote!(#e),
|
|
||||||
_ => todo!(),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Some(quote! {
|
|
||||||
#sig {
|
|
||||||
#trt_name::#fn_name(#(#args),*)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.collect::<TokenStream>();
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
#item
|
|
||||||
|
|
||||||
impl<'a, T> #trt_name for &'a T where T: ?Sized + #trt_name {
|
|
||||||
#fns
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> #trt_name for &'a mut T where T: ?Sized + #trt_name {
|
|
||||||
#fns
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use proc_macro2::TokenStream;
|
|
||||||
use quote::quote;
|
|
||||||
|
|
||||||
use crate::impl_for_refs_core;
|
|
||||||
|
|
||||||
fn expect_output(
|
|
||||||
attrs: TokenStream,
|
|
||||||
input: TokenStream,
|
|
||||||
output: TokenStream,
|
|
||||||
) {
|
|
||||||
assert_eq!(
|
|
||||||
impl_for_refs_core(attrs, input).to_string(),
|
|
||||||
output.to_string()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_works() {
|
|
||||||
expect_output(
|
|
||||||
quote!(),
|
|
||||||
quote! {
|
|
||||||
#[impl_for_refs]
|
|
||||||
trait Test {
|
|
||||||
fn test(&self) -> usize;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
quote! {
|
|
||||||
#[impl_for_refs]
|
|
||||||
trait Test {
|
|
||||||
fn test(&self) -> usize;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Test for &'a T where T: ?Sized + Test {
|
|
||||||
fn test(&self) -> usize {
|
|
||||||
Test::test((&**self))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Test for &'a mut T where T: ?Sized + Test {
|
|
||||||
fn test(&self) -> usize {
|
|
||||||
Test::test((&**self))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
use e_impl_for_refs_core::impl_for_refs_core;
|
|
||||||
use proc_macro::TokenStream;
|
|
||||||
use proc_macro_error::proc_macro_error;
|
|
||||||
|
|
||||||
#[proc_macro_error]
|
|
||||||
#[proc_macro_attribute]
|
|
||||||
pub fn impl_for_refs(attrs: TokenStream, item: TokenStream) -> TokenStream {
|
|
||||||
impl_for_refs_core(attrs.into(), item.into()).into()
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue