Skip to content

Commit 902217a

Browse files
authored
Merge pull request #6 from greyblake/refactor-gen
Refactor code generation
2 parents 28c4cbf + 7ae6b08 commit 902217a

File tree

9 files changed

+277
-10
lines changed

9 files changed

+277
-10
lines changed

kinded/src/errors.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/// An error which is returned when parsing of a kind type failures.
2+
#[derive(Debug)]
3+
pub struct ParseKindError {
4+
kind_type_name: String,
5+
given_string: String,
6+
}
7+
8+
impl ParseKindError {
9+
/// This method is used by `kinded` macro to construct an error for FromStr trait and is not
10+
/// recommend for a direct usage by users.
11+
pub fn from_type_name_and_string(
12+
kind_type_name: String,
13+
given_string: String,
14+
) -> ParseKindError {
15+
ParseKindError {
16+
kind_type_name,
17+
given_string,
18+
}
19+
}
20+
}
21+
22+
impl ::core::fmt::Display for ParseKindError {
23+
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
24+
let Self {
25+
kind_type_name,
26+
given_string,
27+
} = self;
28+
write!(
29+
f,
30+
r#"Failed to parse {kind_type_name} from "{given_string}""#
31+
)
32+
}
33+
}
34+
35+
impl ::std::error::Error for ParseKindError {
36+
fn source(&self) -> Option<&(dyn ::std::error::Error + 'static)> {
37+
None
38+
}
39+
}

kinded/src/lib.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -164,11 +164,9 @@
164164
//!
165165
//! MIT © [Serhii Potapov](https://www.greyblake.com)
166166
167-
pub use kinded_macros::Kinded;
168-
use std::fmt::Debug;
169-
170-
pub trait Kinded {
171-
type Kind: PartialEq + Eq + Debug + Clone + Copy;
167+
mod errors;
168+
mod traits;
172169

173-
fn kind(&self) -> Self::Kind;
174-
}
170+
pub use errors::ParseKindError;
171+
pub use kinded_macros::Kinded;
172+
pub use traits::Kinded;

kinded/src/traits.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
use ::core::fmt::Debug;
2+
3+
/// A trait that can be implemented by a main enum type.
4+
/// Typically should be derived with `#[derive(kinded::Kinded)]`.
5+
pub trait Kinded {
6+
type Kind: PartialEq + Eq + Debug + Clone + Copy;
7+
8+
fn kind(&self) -> Self::Kind;
9+
}

kinded_macros/src/gen/kind_enum.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
use crate::models::{DisplayCase, Meta};
2+
use proc_macro2::{Ident, TokenStream};
3+
use quote::quote;
4+
5+
pub fn gen_kind_enum(meta: &Meta) -> TokenStream {
6+
let kind_enum_definition = gen_definition(meta);
7+
let impl_from_traits = gen_impl_from_traits(meta);
8+
let impl_display_trait = gen_impl_display_trait(meta);
9+
10+
quote!(
11+
#kind_enum_definition
12+
#impl_from_traits
13+
#impl_display_trait
14+
)
15+
}
16+
17+
fn gen_definition(meta: &Meta) -> TokenStream {
18+
let vis = &meta.vis;
19+
let kind_name = meta.kind_name();
20+
let variant_names: Vec<&Ident> = meta.variants.iter().map(|v| &v.ident).collect();
21+
let traits = meta.derive_traits();
22+
23+
quote!(
24+
#[derive(#(#traits),*)] // #[derive(Debug, Clone, Copy, PartialEq, Eq)]
25+
#vis enum #kind_name { // pub enum DrinkKind {
26+
#(#variant_names),* // Mate, Coffee, Tea
27+
} // }
28+
29+
impl #kind_name { // impl DrinkKind {
30+
pub fn all() -> impl Iterator<Item = #kind_name> { // pub fn all() -> impl Iterator<Item = DrinkKind> {
31+
[ // [
32+
#(#kind_name::#variant_names),* // DrinkKind::Mate, DrinkKind::Coffee, DrinkKind::Tea
33+
].into_iter() // ]
34+
} // }
35+
} // }
36+
)
37+
}
38+
39+
fn gen_impl_from_traits(meta: &Meta) -> TokenStream {
40+
let kind_name = meta.kind_name();
41+
let generics = &meta.generics;
42+
let main_enum_with_generics = meta.main_enum_with_generics();
43+
44+
quote!(
45+
impl #generics From<#main_enum_with_generics> for #kind_name { // impl<T> From<Drink<T>> for DrinkKind {
46+
fn from(value: #main_enum_with_generics) -> #kind_name { // fn from(value: Drink<T>) -> DrinkKind {
47+
value.kind() // value.kind()
48+
} // }
49+
} // }
50+
51+
impl #generics From<&#main_enum_with_generics> for #kind_name { // impl<T> From<Drink<T>> for DrinkKind {
52+
fn from(value: &#main_enum_with_generics) -> #kind_name { // fn from(value: &Drink<T>) -> DrinkKind {
53+
value.kind() // value.kind()
54+
} // }
55+
} // }
56+
)
57+
}
58+
59+
fn gen_impl_display_trait(meta: &Meta) -> TokenStream {
60+
let kind_name = meta.kind_name();
61+
let maybe_case = meta.kinded_attrs.display;
62+
63+
let match_branches = meta.variants.iter().map(|variant| {
64+
let original_variant_name_str = variant.ident.to_string();
65+
let cased_variant_name = apply_display_case(original_variant_name_str, maybe_case);
66+
let variant_name = &variant.ident;
67+
quote!(
68+
#kind_name::#variant_name => write!(f, #cased_variant_name)
69+
)
70+
});
71+
72+
quote!(
73+
impl std::fmt::Display for #kind_name { // impl std::fmt::Display for DrinkKind {
74+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75+
match self { // match self {
76+
#(#match_branches),* // DrinkKind::Mate => write!(f, "mate"),
77+
} // }
78+
} // }
79+
} //
80+
)
81+
}
82+
83+
fn apply_display_case(original: String, maybe_display_case: Option<DisplayCase>) -> String {
84+
use convert_case::{Case, Casing};
85+
86+
if let Some(display_case) = maybe_display_case {
87+
let case: Case = display_case.into();
88+
original.to_case(case)
89+
} else {
90+
original
91+
}
92+
}

kinded_macros/src/gen/main_enum.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
use crate::models::{FieldsType, Meta, Variant};
2+
use proc_macro2::{Ident, TokenStream};
3+
use quote::quote;
4+
5+
pub fn gen_main_enum_extra(meta: &Meta) -> TokenStream {
6+
let fn_kind = gen_fn_kind(meta);
7+
let main_enum_with_generics = meta.main_enum_with_generics();
8+
let generics = &meta.generics;
9+
10+
let impl_kinded_trait = gen_impl_kinded_trait(meta);
11+
12+
quote!(
13+
impl #generics #main_enum_with_generics { // impl<T> Drink<T> {
14+
#fn_kind // fn kind(&self) -> DrinkKind { ... }
15+
} // }
16+
17+
#impl_kinded_trait // impl<T> ::kinded::Kinded for Drink<T> { .. }
18+
)
19+
}
20+
21+
fn gen_fn_kind(meta: &Meta) -> TokenStream {
22+
let name = &meta.ident;
23+
let kind_name = meta.kind_name();
24+
let match_branches = meta
25+
.variants
26+
.iter()
27+
.map(|variant| gen_match_branch(name, &kind_name, variant));
28+
29+
quote!(
30+
pub fn kind(&self) -> #kind_name { // pub fn kind(&self) -> DrinkKind {
31+
match self { // match self {
32+
#(#match_branches),* // Drink::Coffee(..) => DrinkKind::Coffee,
33+
} // }
34+
} // }
35+
)
36+
}
37+
38+
fn gen_match_branch(name: &Ident, kind_name: &Ident, variant: &Variant) -> TokenStream {
39+
let variant_name = &variant.ident;
40+
let variant_destruct = match variant.fields_type {
41+
FieldsType::Named => quote!({ .. }),
42+
FieldsType::Unnamed => quote!((..)),
43+
FieldsType::Unit => quote!(),
44+
};
45+
46+
quote!(
47+
#name::#variant_name #variant_destruct => #kind_name::#variant_name
48+
)
49+
}
50+
51+
fn gen_impl_kinded_trait(meta: &Meta) -> TokenStream {
52+
let kind_name = meta.kind_name();
53+
let main_enum_with_generics = meta.main_enum_with_generics();
54+
let generics = &meta.generics;
55+
56+
quote!(
57+
impl #generics ::kinded::Kinded for #main_enum_with_generics { // impl<T> ::kinded::Kinded for Drink<T> {
58+
type Kind = #kind_name; // type Kind = DrinkKind;
59+
//
60+
fn kind(&self) -> #kind_name { // fn kind(&self) -> DrinkKind {
61+
self.kind() // self.kind()
62+
} // }
63+
} // }
64+
)
65+
}

kinded_macros/src/gen/mod.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
mod kind_enum;
2+
mod main_enum;
3+
4+
use crate::models::Meta;
5+
use proc_macro2::TokenStream;
6+
use quote::quote;
7+
8+
pub fn generate(meta: Meta) -> TokenStream {
9+
let kind_enum = kind_enum::gen_kind_enum(&meta);
10+
let main_enum_extra = main_enum::gen_main_enum_extra(&meta);
11+
12+
quote!(
13+
#kind_enum
14+
#main_enum_extra
15+
)
16+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use quote::quote;
55
pub fn generate(meta: Meta) -> TokenStream {
66
let enum_kind = gen_enum_kind(&meta);
77
let impl_display_for_enum_kind = gen_impl_display_for_enum_kind(&meta);
8+
let impl_from_str_for_enum_kind = gen_impl_from_str_for_enum_kind(&meta);
89

910
let fn_kind = gen_fn_kind(&meta);
1011
let type_name = &meta.ident;
@@ -18,6 +19,8 @@ pub fn generate(meta: Meta) -> TokenStream {
1819

1920
#impl_display_for_enum_kind // impl std::fmt::Display for DrinkKind { ... }
2021

22+
#impl_from_str_for_enum_kind // impl std::str::FromStr for DrinkKind { ... }
23+
2124
impl #generics #type_with_generics { // impl<T> Drink<T> {
2225
#fn_kind // fn kind(&self) -> DrinkKind { ... }
2326
} // }
@@ -90,6 +93,30 @@ fn gen_impl_display_for_enum_kind(meta: &Meta) -> TokenStream {
9093
)
9194
}
9295

96+
fn gen_impl_from_str_for_enum_kind(meta: &Meta) -> TokenStream {
97+
let kind_name = meta.kind_name();
98+
// let maybe_case = meta.kinded_attrs.display;
99+
100+
// let match_branches = meta.variants.iter().map(|variant| {
101+
// let original_variant_name_str = variant.ident.to_string();
102+
// let cased_variant_name = apply_display_case(original_variant_name_str, maybe_case);
103+
// let variant_name = &variant.ident;
104+
// quote!(
105+
// #kind_name::#variant_name => write!(f, #cased_variant_name)
106+
// )
107+
// });
108+
109+
quote!(
110+
impl ::std::str::FromStr for #kind_name {
111+
type Err = &'static str;
112+
113+
fn from_str(s: &str) -> Result<Self, Self::Err> {
114+
todo!()
115+
}
116+
}
117+
)
118+
}
119+
93120
fn apply_display_case(original: String, maybe_display_case: Option<DisplayCase>) -> String {
94121
use convert_case::{Case, Casing};
95122

kinded_macros/src/models.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use proc_macro2::Ident;
2-
use quote::format_ident;
1+
use proc_macro2::{Ident, TokenStream};
2+
use quote::{format_ident, quote};
33
use syn::{Generics, Path, Visibility};
44

55
#[derive(Debug)]
@@ -48,6 +48,13 @@ impl Meta {
4848

4949
traits
5050
}
51+
52+
pub fn main_enum_with_generics(&self) -> TokenStream {
53+
let type_name = &self.ident;
54+
let generics = &self.generics;
55+
56+
quote!(#type_name #generics)
57+
}
5158
}
5259

5360
#[derive(Debug)]

test_suite/src/lib.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ mod kind_enum {
9595
assert_eq!(RoleKind::from(&guest), RoleKind::Guest);
9696
}
9797

98-
mod display {
98+
mod display_trait {
9999
use super::RoleKind;
100100

101101
#[test]
@@ -206,6 +206,20 @@ mod kind_enum {
206206
assert_eq!(DrinkKind::HotMate.to_string(), "HOTMATE")
207207
}
208208
}
209+
210+
mod from_str_trait {
211+
#[derive(kinded::Kinded)]
212+
enum Mate {
213+
HotMate,
214+
Terere,
215+
}
216+
217+
// #[test]
218+
// fn should_implement_from_str_trait() {
219+
// let kind: MateKind = "Terere".parse().unwrap();
220+
// assert_eq!(kind, MateKind::Terere);
221+
// }
222+
}
209223
}
210224

211225
#[test]

0 commit comments

Comments
 (0)