iridis_message_derive/
lib.rs1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::{
6 parse_macro_input, punctuated::Punctuated, token::Comma, DeriveInput, Field, Fields, Ident,
7 Token, Variant,
8};
9
10#[proc_macro_derive(ArrowMessage)]
11pub fn from_into_arrow_derive(input: TokenStream) -> TokenStream {
12 let input = parse_macro_input!(input as DeriveInput);
13
14 let name = input.ident;
15
16 match input.data {
17 syn::Data::Struct(s) => match s.fields {
18 Fields::Named(ref fields) => struct_derive(name, fields.named.clone()),
19 _ => panic!("Only structs with named fields are supported"),
20 },
21 syn::Data::Enum(e) => enum_derive(name, e.variants.clone()),
22 _ => panic!("Only structs and enums are supported"),
23 }
24}
25
26fn struct_derive(name: Ident, fields: Punctuated<Field, Comma>) -> TokenStream {
27 let field_attributes = fields
28 .iter()
29 .map(|field| (&field.ident, &field.ty))
30 .collect::<Vec<_>>();
31
32 let fields_fill = field_attributes.iter().map(|&(field, ty)| {
33 quote! {
34 <#ty>::field(stringify!(#field)),
35 }
36 });
37
38 let union_data_fill = field_attributes.iter().map(|&(field, _)| {
39 quote! {
40 #field: extract_union_data(stringify!(#field), &map, &children)?,
41 }
42 });
43
44 let arrow_data_fill = field_attributes.iter().map(|&(field, _)| {
45 quote! {
46 self.#field.try_into_arrow()?,
47 }
48 });
49
50 let expanded = quote! {
51 impl ArrowMessage for #name {
52 fn field(name: impl Into<String>) -> iridis_message::prelude::thirdparty::arrow_schema::Field {
53 make_union_fields(
54 name,
55 vec![
56 #(#fields_fill)*
57 ],
58 )
59 }
60
61 fn try_from_arrow(data: iridis_message::prelude::thirdparty::arrow_data::ArrayData) -> iridis_message::prelude::thirdparty::eyre::Result<Self>
62 where
63 Self: Sized,
64 {
65 let (map, children) = unpack_union(data);
66
67 Ok(Self {
68 #(#union_data_fill)*
69 })
70 }
71
72 fn try_into_arrow(self) -> iridis_message::prelude::thirdparty::eyre::Result<iridis_message::prelude::thirdparty::arrow_array::ArrayRef> {
73 let union_fields = get_union_fields::<Self>()?;
74
75 make_union_array(
76 union_fields,
77 vec![
78 #(#arrow_data_fill)*
79 ],
80 )
81 }
82 }
83
84 impl TryFrom<iridis_message::prelude::thirdparty::arrow_data::ArrayData> for #name {
85 type Error = iridis_message::prelude::thirdparty::eyre::Report;
86
87 fn try_from(data: iridis_message::prelude::thirdparty::arrow_data::ArrayData) -> iridis_message::prelude::thirdparty::eyre::Result<Self> {
88 #name::try_from_arrow(data)
89 }
90 }
91
92 impl TryFrom<#name> for iridis_message::prelude::thirdparty::arrow_data::ArrayData {
93 type Error = iridis_message::prelude::thirdparty::eyre::Report;
94
95 fn try_from(item: #name) -> iridis_message::prelude::thirdparty::eyre::Result<Self> {
96 use iridis_message::prelude::thirdparty::arrow_array::Array;
97
98 item.try_into_arrow().map(|array| array.into_data())
99 }
100 }
101 };
102
103 TokenStream::from(expanded)
104}
105
106fn enum_derive(name: Ident, variants: Punctuated<Variant, Token![,]>) -> TokenStream {
107 let variants: Vec<_> = variants
108 .iter()
109 .map(|variant| {
110 let variant_name = &variant.ident;
111 let variant_str = variant_name.to_string().to_lowercase(); (variant_name, variant_str)
113 })
114 .collect();
115
116 let into_string_arms = variants.iter().map(|(variant_name, variant_str)| {
117 quote! {
118 #name::#variant_name => #variant_str.to_string(),
119 }
120 });
121
122 let try_from_string_arms = variants.iter().map(|(variant_name, variant_str)| {
123 quote! {
124 #variant_str => Ok(#name::#variant_name),
125 }
126 });
127
128 let expanded = quote! {
129 impl #name {
130 pub fn into_string(self) -> String {
131 match self {
132 #(#into_string_arms)*
133 }
134 }
135
136 pub fn try_from_string(s: String) -> iridis_message::prelude::thirdparty::eyre::Result<Self> {
137 match s.as_str() {
138 #(#try_from_string_arms)*
139 _ => Err(iridis_message::prelude::thirdparty::eyre::eyre!("Invalid value for {}: {}", stringify!(#name), s)),
140 }
141 }
142 }
143
144 impl ArrowMessage for #name {
145 fn field(name: impl Into<String>) -> iridis_message::prelude::thirdparty::arrow_schema::Field {
146 String::field(name)
147 }
148
149 fn try_from_arrow(data: iridis_message::prelude::thirdparty::arrow_data::ArrayData) -> iridis_message::prelude::thirdparty::eyre::Result<Self>
150 where
151 Self: Sized,
152 {
153 Encoding::try_from_string(String::try_from_arrow(data)?)
154 }
155
156 fn try_into_arrow(self) -> iridis_message::prelude::thirdparty::eyre::Result<iridis_message::prelude::thirdparty::arrow_array::ArrayRef> {
157 String::try_into_arrow(self.into_string())
158 }
159 }
160
161
162 impl TryFrom<iridis_message::prelude::thirdparty::arrow_data::ArrayData> for #name {
163 type Error = iridis_message::prelude::thirdparty::eyre::Report;
164
165 fn try_from(data: iridis_message::prelude::thirdparty::arrow_data::ArrayData) -> iridis_message::prelude::thirdparty::eyre::Result<Self> {
166 #name::try_from_arrow(data)
167 }
168 }
169
170 impl TryFrom<#name> for iridis_message::prelude::thirdparty::arrow_data::ArrayData {
171 type Error = iridis_message::prelude::thirdparty::eyre::Report;
172
173 fn try_from(item: #name) -> iridis_message::prelude::thirdparty::eyre::Result<Self> {
174 item.try_into_arrow().map(|array| array.into_data())
175 }
176 }
177 };
178
179 TokenStream::from(expanded)
180}