oct_macros/
lib.rs

1// Copyright 2024-2025 Gabriel Bjørnager Jensen.
2//
3// This Source Code Form is subject to the terms of
4// the Mozilla Public License, v. 2.0. If a copy of
5// the MPL was not distributed with this file, you
6// can obtain one at:
7// <https://mozilla.org/MPL/2.0/>.
8
9#![doc(html_logo_url = "https://gitlab.com/bjoernager/oct/-/raw/master/doc-icon.svg")]
10
11//! This crate implements procedural macros for [`oct`](https://crates.io/crates/oct/).
12
13// For use in macros:
14extern crate self as oct_macros;
15
16use proc_macro::TokenStream;
17use quote::quote;
18use syn::{
19	parse_macro_input,
20	parse2,
21	Data,
22	DeriveInput,
23	Type,
24};
25use syn::parse::Parse;
26
27mod derive_impl;
28
29mod discriminants;
30mod repr;
31
32use discriminants::Discriminants;
33use repr::Repr;
34
35#[proc_macro_derive(Decode, attributes(oct))]
36pub fn derive_decode(input: TokenStream) -> TokenStream {
37	let input = parse_macro_input!(input as DeriveInput);
38
39	let self_name = input.ident;
40
41	let mut error: Type = parse2(quote! { ::oct::error::GenericDecodeError }).unwrap();
42
43	for attr in &input.attrs {
44		if attr.meta.path().is_ident("oct") {
45			let _ = attr.parse_nested_meta(|meta| {
46				if meta.path.is_ident("decode_error") {
47					error = Parse::parse(meta.value()?)?;
48				}
49
50				Ok(())
51			});
52		}
53	}
54
55	let body = match input.data {
56		Data::Struct(data) => derive_impl::decode_struct(data, error),
57
58		Data::Enum(data) => {
59			let repr = Repr::get(&input.attrs).unwrap_or_default();
60
61			derive_impl::decode_enum(data, repr, error)
62		}
63
64		Data::Union(_) => panic!("untagged union `{self_name}` cannot derive `oct::decode::Decode`"),
65	};
66
67	let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
68
69	let output = quote! {
70		#[automatically_derived]
71		impl #impl_generics ::oct::decode::Decode for #self_name #ty_generics
72		#where_clause
73		{
74			#body
75		}
76	};
77
78	output.into()
79}
80
81#[proc_macro_derive(Encode, attributes(oct))]
82pub fn derive_encode(input: TokenStream) -> TokenStream {
83	let input = parse_macro_input!(input as DeriveInput);
84
85	let self_name = input.ident;
86
87	let mut error: Type = parse2(quote! { ::oct::error::GenericEncodeError }).unwrap();
88
89	for attr in &input.attrs {
90		if attr.meta.path().is_ident("oct") {
91			let _ = attr.parse_nested_meta(|meta| {
92				if meta.path.is_ident("encode_error") {
93					error = Parse::parse(meta.value()?)?;
94				}
95
96				Ok(())
97			});
98		}
99	}
100
101	let body = match input.data {
102		Data::Struct(data) => derive_impl::encode_struct(data, error),
103
104		Data::Enum(data) => {
105			let repr = Repr::get(&input.attrs).unwrap_or_default();
106
107			derive_impl::encode_enum(data, repr, error)
108		}
109
110		Data::Union(_) => panic!("untagged union `{self_name}` cannot derive `oct::encode::Encode`"),
111	};
112
113	let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
114
115	let output = quote! {
116		#[automatically_derived]
117		impl #impl_generics ::oct::encode::Encode for #self_name #ty_generics
118		#where_clause
119		{
120			#body
121		}
122	};
123
124	output.into()
125}
126
127#[proc_macro_derive(SizedEncode)]
128pub fn derive_sized_encode(input: TokenStream) -> TokenStream {
129	let input = parse_macro_input!(input as DeriveInput);
130
131	let self_name = input.ident;
132
133	let body = match input.data {
134		Data::Struct(data) => derive_impl::sized_encode_struct(data),
135
136		Data::Enum(data) => {
137			let repr = Repr::get(&input.attrs).unwrap_or_default();
138
139			derive_impl::sized_encode_enum(data, repr)
140		}
141
142		Data::Union(_) => panic!("untagged union `{self_name}` cannot derive `oct::encode::SizedEncode`"),
143	};
144
145	let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
146
147	let output = quote! {
148		#[automatically_derived]
149		impl #impl_generics ::oct::encode::SizedEncode for #self_name #ty_generics
150		#where_clause
151		{
152			#body
153		}
154	};
155
156	output.into()
157}