Skip to main content

humphrey_json_derive/
lib.rs

1//! Provides the derive macros for Humphrey JSON.
2//!
3//! These macros should only be used from the Humphrey JSON crate itself, never directly from this crate. You can read about their uses in the [Humphrey JSON documentation](https://humphrey.whenderson.dev/json/data-structures.html).
4
5#![warn(missing_docs)]
6#![warn(clippy::missing_docs_in_private_items)]
7
8extern crate proc_macro;
9
10mod enum_type;
11mod named_struct;
12mod tuple_struct;
13
14use proc_macro::TokenStream;
15
16use syn::spanned::Spanned;
17use syn::{parse_macro_input, Data, DeriveInput, Error};
18
19/// Derives the `FromJson` trait for a type.
20///
21/// This macro can be used on named structs, tuple structs, and enums. It is not currently supported for enums with data variants.
22#[proc_macro_derive(FromJson, attributes(rename))]
23pub fn derive_from_json(input: TokenStream) -> TokenStream {
24    let ast = parse_macro_input!(input as DeriveInput);
25
26    match &ast.data.clone() {
27        Data::Struct(r#struct) => {
28            if r#struct
29                .fields
30                .iter()
31                .map(|field| field.ident.clone())
32                .all(|ident| ident.is_some())
33                && !r#struct.fields.is_empty()
34            {
35                named_struct::from_json_named_struct(ast, r#struct)
36            } else if !r#struct.fields.is_empty() {
37                tuple_struct::from_json_tuple_struct(ast, r#struct)
38            } else {
39                Error::new(ast.span(), "`FromJson` cannot be derived for empty structs")
40                    .to_compile_error()
41                    .into()
42            }
43        }
44
45        Data::Enum(r#enum) => {
46            if !r#enum
47                .variants
48                .iter()
49                .any(|variant| !variant.fields.is_empty())
50            {
51                enum_type::from_json_enum(ast, r#enum)
52            } else {
53                Error::new(
54                    ast.span(),
55                    "`FromJson` cannot be derived for enums with data variants",
56                )
57                .to_compile_error()
58                .into()
59            }
60        }
61
62        _ => Error::new(
63            ast.span(),
64            "`FromJson` can only be derived for non-empty structs and enums",
65        )
66        .to_compile_error()
67        .into(),
68    }
69}
70
71/// Derives the `IntoJson` trait for a type.
72///
73/// This macro can be used on named structs, tuple structs, and enums. It is not currently supported for enums with data variants.
74#[proc_macro_derive(IntoJson, attributes(rename))]
75pub fn derive_into_json(input: TokenStream) -> TokenStream {
76    let ast = parse_macro_input!(input as DeriveInput);
77
78    match &ast.data.clone() {
79        Data::Struct(r#struct) => {
80            if r#struct
81                .fields
82                .iter()
83                .map(|field| field.ident.clone())
84                .all(|ident| ident.is_some())
85                && !r#struct.fields.is_empty()
86            {
87                named_struct::into_json_named_struct(ast, r#struct)
88            } else if !r#struct.fields.is_empty() {
89                tuple_struct::into_json_tuple_struct(ast, r#struct)
90            } else {
91                Error::new(ast.span(), "`IntoJson` cannot be derived for empty structs")
92                    .to_compile_error()
93                    .into()
94            }
95        }
96
97        Data::Enum(r#enum) => {
98            if !r#enum
99                .variants
100                .iter()
101                .any(|variant| !variant.fields.is_empty())
102            {
103                enum_type::into_json_enum(ast, r#enum)
104            } else {
105                Error::new(
106                    ast.span(),
107                    "`IntoJson` cannot be derived for enums with data variants",
108                )
109                .to_compile_error()
110                .into()
111            }
112        }
113
114        _ => Error::new(
115            ast.span(),
116            "`IntoJson` can only be derived for non-empty structs and enums",
117        )
118        .to_compile_error()
119        .into(),
120    }
121}