type_id_derive_impl/
lib.rs1use quote2::{
2 proc_macro2::{Delimiter, Group},
3 quote, IntoTokens, Quote, Token,
4};
5use syn::{
6 __private::{Span, TokenStream2 as TokenStream},
7 spanned::Spanned,
8 *,
9};
10
11pub fn expand(crate_path: impl IntoTokens, input: &DeriveInput, output: &mut TokenStream) {
12 let DeriveInput {
13 attrs,
14 ident,
15 generics,
16 data,
17 ..
18 } = input;
19
20 let doc = get_comments_from(attrs);
21 let fmt_str = format!("{{}}::{ident}");
22
23 if let Some(param) = generics.type_params().next() {
24 return output.extend(
25 Error::new(param.span(), "Support for generic type isn't complete yet.")
26 .to_compile_error(),
27 );
28 }
29
30 let mut body = TokenStream::new();
31 let kind = match data {
32 Data::Struct(data) => match &data.fields {
33 Fields::Named(fields) => {
34 to_object(&mut body, fields);
35 "Struct"
36 }
37 Fields::Unnamed(fields) => {
38 to_tuple(&mut body, fields);
39 "Tuple"
40 }
41 Fields::Unit => panic!("`{ident}` struct needs at most one field"),
42 },
43 Data::Enum(data) => {
44 let is_unit = data
45 .variants
46 .iter()
47 .all(|v| v.discriminant.is_some() || matches!(v.fields, Fields::Unit));
48
49 let variants = data
50 .variants
51 .iter()
52 .map(|v| (get_comments_from(&v.attrs), v.ident.to_string(), v));
53
54 if is_unit {
55 let mut value: isize = -1;
56 for (doc, name, v) in variants {
57 value = match &v.discriminant {
58 Some((_, expr)) => parse_int(expr),
59 None => value + 1,
60 };
61 quote!(body, {
62 __crate::UnitField::new(#doc, #name, #value),
63 });
64 }
65 "Unit"
66 } else {
67 for (doc, name, v) in variants {
68 let kind = quote(|o| match &v.fields {
69 Fields::Named(fields) => {
70 let body = quote(|o| to_object(o, fields));
71 quote!(o, { Struct(::std::vec![#body]) });
72 }
73 Fields::Unnamed(fields) => {
74 let body = quote(|o| to_tuple(o, fields));
75 quote!(o, { Tuple(::std::vec![#body]) });
76 }
77 Fields::Unit => {
78 quote!(o, { Unit });
79 }
80 });
81 quote!(body, {
82 __crate::EnumField::new(#doc, #name, __crate::EnumKind::#kind),
83 });
84 }
85 "Enum"
86 }
87 }
88 Data::Union(_) => panic!("`Message` implementation for `union` is not yet stabilized"),
89 };
90
91 let kind = Ident::new(kind, Span::call_site());
92 let body = Token(Group::new(Delimiter::Bracket, body));
93 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
94
95 quote!(output, {
96 const _: () = {
97 use #crate_path as __crate;
98 impl #impl_generics __crate::TypeId for #ident #ty_generics #where_clause {
99 fn ty(__c: &mut __crate::CostomTypes) -> __crate::Ty {
100 __c.register(
101 ::std::format!(#fmt_str, ::std::module_path!()),
102 |__c| __crate::CustomTypeKind::#kind(__crate::CustomType::new(#doc, ::std::vec!#body))
103 )
104 }
105 }
106 };
107 });
108}
109
110fn to_tuple(body: &mut TokenStream, fields: &FieldsUnnamed) {
111 for Field { attrs, ty, .. } in &fields.unnamed {
112 let doc: String = get_comments_from(attrs);
113 quote!(body, {
114 __crate::TupleField::new(#doc, <#ty as __crate::TypeId>::ty(__c)),
115 });
116 }
117}
118
119fn to_object(body: &mut TokenStream, fields: &FieldsNamed) {
120 for Field {
121 attrs, ident, ty, ..
122 } in &fields.named
123 {
124 let doc = get_comments_from(attrs);
125 let ident = ident.as_ref().map(|v| v.to_string());
126 quote!(body, {
127 __crate::StructField::new(#doc, #ident, <#ty as __crate::TypeId>::ty(__c)),
128 });
129 }
130}
131
132fn parse_int(expr: &Expr) -> isize {
133 match expr {
134 Expr::Lit(expr_lit) => match &expr_lit.lit {
135 Lit::Int(int) => int.base10_parse().unwrap(),
136 _ => panic!("Expect integer"),
137 },
138 _ => panic!("Not a number"),
139 }
140}
141
142fn get_comments_from(attrs: &Vec<Attribute>) -> String {
143 let mut string = String::new();
144 for attr in attrs {
145 if let Meta::NameValue(MetaNameValue { path, value, .. }) = &attr.meta {
146 if path.is_ident("doc") {
147 if let Expr::Lit(expr) = value {
148 if let Lit::Str(data) = &expr.lit {
149 string += &data.value();
150 string += "\n"
151 }
152 }
153 }
154 }
155 }
156 string
157}