Skip to main content

reifydb_macro_impl/
operator_state.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use proc_macro2::TokenStream;
5use quote::{ToTokens, quote};
6use syn::{Data, DeriveInput, Error, Fields, Path, Type, parse_str, parse2};
7
8fn field_types(data: &Data) -> Vec<&Type> {
9	let fields: Vec<&Fields> = match data {
10		Data::Struct(item) => vec![&item.fields],
11		Data::Enum(item) => item.variants.iter().map(|variant| &variant.fields).collect(),
12		Data::Union(item) => return item.fields.named.iter().map(|field| &field.ty).collect(),
13	};
14	let mut seen = Vec::new();
15	let mut types = Vec::new();
16	for field in fields.into_iter().flat_map(|fields| fields.iter()) {
17		let rendered = field.ty.to_token_stream().to_string();
18		if !seen.contains(&rendered) {
19			seen.push(rendered);
20			types.push(&field.ty);
21		}
22	}
23	types
24}
25
26fn field_bounds(data: &Data, trait_path: &str) -> String {
27	field_types(data)
28		.into_iter()
29		.map(|ty| format!("{}: {trait_path}", ty.to_token_stream()))
30		.collect::<Vec<_>>()
31		.join(", ")
32}
33
34pub fn operator_state_impl(attr: TokenStream, item: TokenStream, crate_path: &str) -> TokenStream {
35	if !attr.is_empty() {
36		return Error::new_spanned(attr, "operator_state accepts no argument").to_compile_error();
37	}
38	let input: DeriveInput = match parse2(item.clone()) {
39		Ok(input) => input,
40		Err(err) => return err.to_compile_error(),
41	};
42	let root: Path = match parse_str(crate_path) {
43		Ok(path) => path,
44		Err(err) => return err.to_compile_error(),
45	};
46	let name = &input.ident;
47	let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
48	let serde_crate = format!("{crate_path}::row::operator::state::derive::serde");
49	let serialize_bound = field_bounds(&input.data, &format!("{serde_crate}::Serialize"));
50	let deserialize_bound = field_bounds(&input.data, &format!("{serde_crate}::de::DeserializeOwned"));
51	let extra_bounds = quote! {
52		Self: #root::row::operator::state::StateCodec,
53	};
54	let merged_where = match where_clause {
55		Some(existing) => {
56			let predicates = &existing.predicates;
57			quote! { where #predicates, #extra_bounds }
58		}
59		None => quote! { where #extra_bounds },
60	};
61
62	quote! {
63		#[derive(
64			#root::row::operator::state::derive::Serialize,
65			#root::row::operator::state::derive::Deserialize,
66		)]
67		#[serde(crate = #serde_crate)]
68		#[serde(bound(serialize = #serialize_bound, deserialize = #deserialize_bound))]
69		#item
70
71		#[automatically_derived]
72		impl #impl_generics #root::row::operator::state::OperatorState for #name #ty_generics #merged_where {
73			fn encode_state(
74				&self,
75			) -> ::core::result::Result<#root::row::pod::EncodedPodRow, #root::row::operator::state::StateError> {
76				#root::row::operator::state::encode(self)
77			}
78
79			fn decode_state(
80				bytes: &#root::row::pod::EncodedPodRow,
81			) -> ::core::result::Result<Self, #root::row::operator::state::StateError> {
82				#root::row::operator::state::decode_body::<Self>(bytes)
83			}
84		}
85	}
86}