Skip to main content

osal_rs_serde_derive/
lib.rs

1/***************************************************************************
2 *
3 * osal-rs-serde-derive
4 * Copyright (C) 2026 Antonio Salsi <passy.linux@zresa.it>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
18 *
19 ***************************************************************************/
20
21//! Derive macros for osal-rs-serde
22//!
23//! This crate provides `#[derive(Serialize, Deserialize)]` macros for automatic
24//! implementation of serialization traits.
25//!
26//! # Examples
27//!
28//! ```ignore
29//! use osal_rs_serde::{Serialize, Deserialize};
30//!
31//! #[derive(Serialize, Deserialize)]
32//! struct SensorData {
33//!     temperature: i16,
34//!     humidity: u8,
35//!     pressure: u32,
36//! }
37//! ```
38
39
40use proc_macro::TokenStream;
41use quote::quote;
42use proc_macro2::Literal;
43use syn::{parse_macro_input, Data, DeriveInput, Fields};
44
45/// Derive macro for the `Serialize` trait.
46///
47/// Automatically implements serialization for structs with named or unnamed fields.
48///
49/// # Examples
50///
51/// ```ignore
52/// #[derive(Serialize)]
53/// struct Point {
54///     x: i32,
55///     y: i32,
56/// }
57/// ```
58#[proc_macro_derive(Serialize)]
59pub fn derive_serialize(input: TokenStream) -> TokenStream {
60    let input = parse_macro_input!(input as DeriveInput);
61    let name = &input.ident;
62
63
64    let serialize_impl = match &input.data {
65        Data::Struct(data_struct) => match &data_struct.fields {
66            Fields::Named(fields) => {
67                let field_count = fields.named.len();
68                let field_serializations = fields.named.iter().map(|f| {
69                    let field_name = &f.ident;
70                    let field_name_str = field_name.as_ref().unwrap().to_string();
71                    quote! {
72                        serializer.serialize_field(#field_name_str, &self.#field_name)?;
73                    }
74                });
75
76                quote! {
77                    impl osal_rs_serde::Serialize for #name {
78                        fn serialize<S: osal_rs_serde::Serializer>(&self, name: &str, serializer: &mut S) -> Result<(), S::Error> {
79                            serializer.serialize_struct_start(name, #field_count)?;
80                            #(#field_serializations)*
81                            serializer.serialize_struct_end()?;
82                            Ok(())
83                        }
84                    }
85                }
86            }
87            Fields::Unnamed(fields) => {
88                let field_count = fields.unnamed.len();
89                let field_serializations = (0..fields.unnamed.len()).map(|i| {
90                    let index = syn::Index::from(i);
91                    let name_lit = Literal::string(&i.to_string());
92                    quote! {
93                        serializer.serialize_field(#name_lit, &self.#index)?;
94                    }
95                });
96
97                quote! {
98                    impl osal_rs_serde::Serialize for #name {
99                        fn serialize<S: osal_rs_serde::Serializer>(&self, name: &str, serializer: &mut S) -> Result<(), S::Error> {
100                            serializer.serialize_struct_start(name, #field_count)?;
101                            #(#field_serializations)*
102                            serializer.serialize_struct_end()?;
103                            Ok(())
104                        }
105                    }
106                }
107            }
108            Fields::Unit => {
109                quote! {
110                    impl osal_rs_serde::Serialize for #name {
111                        fn serialize<S: osal_rs_serde::Serializer>(&self, _name: &str, _serializer: &mut S) -> Result<(), S::Error> {
112                            Ok(())
113                        }
114                    }
115                }
116            }
117        },
118        Data::Enum(_) => {
119            return syn::Error::new_spanned(
120                name,
121                "Serialize derive macro does not support enums yet"
122            )
123            .to_compile_error()
124            .into();
125        }
126        Data::Union(_) => {
127            return syn::Error::new_spanned(
128                name,
129                "Serialize derive macro does not support unions"
130            )
131            .to_compile_error()
132            .into();
133        }
134    };
135
136    TokenStream::from(serialize_impl)
137}
138
139/// Derive macro for the `Deserialize` trait.
140///
141/// Automatically implements deserialization for structs with named or unnamed fields.
142///
143/// # Examples
144///
145/// ```ignore
146/// #[derive(Deserialize)]
147/// struct Point {
148///     x: i32,
149///     y: i32,
150/// }
151/// ```
152#[proc_macro_derive(Deserialize)]
153pub fn derive_deserialize(input: TokenStream) -> TokenStream {
154    let input = parse_macro_input!(input as DeriveInput);
155    let name = &input.ident;
156
157    let deserialize_impl = match &input.data {
158        Data::Struct(data_struct) => match &data_struct.fields {
159            Fields::Named(fields) => {
160                let field_deserializations = fields.named.iter().map(|f| {
161                    let field_name = &f.ident;
162                    let field_name_str = field_name.as_ref().unwrap().to_string();
163                    let field_type = &f.ty;
164                    quote! {
165                        #field_name: deserializer.deserialize_field::<#field_type>(#field_name_str)?
166                    }
167                });
168
169                quote! {
170                    impl osal_rs_serde::Deserialize for #name {
171                        fn deserialize<D: osal_rs_serde::Deserializer>(deserializer: &mut D, name: &str) -> Result<Self, D::Error> {
172                            deserializer.deserialize_struct_start(name)?;
173                            let result = Self {
174                                #(#field_deserializations,)*
175                            };
176                            deserializer.deserialize_struct_end()?;
177                            Ok(result)
178                        }
179                    }
180                }
181            }
182            Fields::Unnamed(fields) => {
183                let field_types = fields.unnamed.iter().map(|f| &f.ty);
184
185                quote! {
186                    impl osal_rs_serde::Deserialize for #name {
187                        fn deserialize<D: osal_rs_serde::Deserializer>(deserializer: &mut D, name: &str) -> Result<Self, D::Error> {
188                            Ok(Self(
189                                #(<#field_types as osal_rs_serde::Deserialize>::deserialize(deserializer, name)?,)*
190                            ))
191                        }
192                    }
193                }
194            }
195            Fields::Unit => {
196                quote! {
197                    impl osal_rs_serde::Deserialize for #name {
198                        fn deserialize<D: osal_rs_serde::Deserializer>(_deserializer: &mut D, _name: &str) -> Result<Self, D::Error> {
199                            Ok(Self)
200                        }
201                    }
202                }
203            }
204        },
205        Data::Enum(_) => {
206            return syn::Error::new_spanned(
207                name,
208                "Deserialize derive macro does not support enums yet"
209            )
210            .to_compile_error()
211            .into();
212        }
213        Data::Union(_) => {
214            return syn::Error::new_spanned(
215                name,
216                "Deserialize derive macro does not support unions"
217            )
218            .to_compile_error()
219            .into();
220        }
221    };
222
223    TokenStream::from(deserialize_impl)
224}