Skip to main content

sacp_cbor_derive/
lib.rs

1//! Procedural macro derives for `sacp-cbor`.
2
3#![deny(clippy::all)]
4#![deny(missing_docs)]
5
6extern crate proc_macro;
7
8mod attrs;
9mod cbor_bytes;
10mod decode;
11mod encode;
12mod types;
13mod util;
14
15use proc_macro::TokenStream;
16use syn::{parse_macro_input, spanned::Spanned, Data, DataEnum, DeriveInput, Ident};
17
18use crate::attrs::parse_cbor_enum_attrs;
19use crate::cbor_bytes::expand as expand_cbor_bytes;
20use crate::decode::{decode_enum, decode_struct};
21use crate::encode::{encode_enum, encode_struct};
22
23fn ensure_non_empty_enum(name: &Ident, data: &DataEnum, derive_name: &str) -> syn::Result<()> {
24    if data.variants.is_empty() {
25        return Err(syn::Error::new(
26            name.span(),
27            format!("{derive_name} does not support empty enums"),
28        ));
29    }
30
31    Ok(())
32}
33
34#[proc_macro_derive(CborEncode, attributes(cbor))]
35/// Derive canonical CBOR encoding for structs and enums.
36pub fn derive_cbor_encode(input: TokenStream) -> TokenStream {
37    let input = parse_macro_input!(input as DeriveInput);
38    let out = (|| -> syn::Result<proc_macro2::TokenStream> {
39        match &input.data {
40            Data::Struct(data) => encode_struct(&input.ident, &input.generics, data),
41            Data::Enum(data) => {
42                ensure_non_empty_enum(&input.ident, data, "CborEncode")?;
43                let attrs = parse_cbor_enum_attrs(&input.attrs)?;
44                encode_enum(&input.ident, &input.generics, data, &attrs)
45            }
46            Data::Union(u) => Err(syn::Error::new(
47                u.union_token.span(),
48                "CborEncode not supported for unions",
49            )),
50        }
51    })();
52
53    match out {
54        Ok(ts) => TokenStream::from(ts),
55        Err(e) => TokenStream::from(e.to_compile_error()),
56    }
57}
58
59#[proc_macro_derive(CborDecode, attributes(cbor))]
60/// Derive canonical CBOR decoding for structs and enums.
61pub fn derive_cbor_decode(input: TokenStream) -> TokenStream {
62    let input = parse_macro_input!(input as DeriveInput);
63    let out = (|| -> syn::Result<proc_macro2::TokenStream> {
64        match &input.data {
65            Data::Struct(data) => decode_struct(&input.ident, &input.generics, data),
66            Data::Enum(data) => {
67                ensure_non_empty_enum(&input.ident, data, "CborDecode")?;
68                let attrs = parse_cbor_enum_attrs(&input.attrs)?;
69                decode_enum(&input.ident, &input.generics, data, &attrs)
70            }
71            Data::Union(u) => Err(syn::Error::new(
72                u.union_token.span(),
73                "CborDecode not supported for unions",
74            )),
75        }
76    })();
77
78    match out {
79        Ok(ts) => TokenStream::from(ts),
80        Err(e) => TokenStream::from(e.to_compile_error()),
81    }
82}
83
84/// Construct canonical CBOR bytes with a JSON-like literal syntax.
85#[proc_macro]
86pub fn cbor_bytes(input: TokenStream) -> TokenStream {
87    expand_cbor_bytes(input)
88}