From f0c8338d487d0f7367f3ec7db5e7eb7541741af2 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Sun, 22 Mar 2026 02:51:14 +0000 Subject: [PATCH] refactor: split TypedObj derives --- --- a/crates/jrsonnet-evaluator/src/arr/spec.rs +++ b/crates/jrsonnet-evaluator/src/arr/spec.rs @@ -609,7 +609,7 @@ } } -#[derive(Typed)] +#[derive(Typed, IntoUntyped)] pub struct KeyValue { key: IStr, value: Thunk, --- a/crates/jrsonnet-evaluator/src/typed/conversions.rs +++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs @@ -2,7 +2,6 @@ use jrsonnet_gcmodule::Trace; use jrsonnet_interner::{IBytes, IStr}; -pub use jrsonnet_macros::Typed; use jrsonnet_types::{ComplexValType, ValType}; use crate::{ @@ -14,6 +13,19 @@ ObjValue, ObjValueBuilder, Result, ResultExt, Thunk, Val, }; +#[doc(hidden)] +pub mod __typed_macro_prelude { + pub use ::jrsonnet_evaluator::{ + error::{ErrorKind, Result as JrResult}, + typed::{ + CheckType, ComplexValType, FromUntyped, IntoUntyped, ParseTypedObj, SerializeTypedObj, + Typed, + }, + IStr, ObjValue, ObjValueBuilder, State, Val, + }; +} +pub use jrsonnet_macros::{FromUntyped, IntoUntyped, Typed}; + #[derive(Trace)] struct ThunkFromUntyped(PhantomData K>); impl ThunkMapper for ThunkFromUntyped @@ -49,9 +61,11 @@ } } -pub trait TypedObj: Typed { - fn serialize(self, out: &mut ObjValueBuilder) -> Result<()>; +pub trait ParseTypedObj: Typed { fn parse(obj: &ObjValue) -> Result; +} +pub trait SerializeTypedObj: Typed { + fn serialize(self, out: &mut ObjValueBuilder) -> Result<()>; fn into_object(self) -> Result { let mut builder = ObjValueBuilder::new(); self.serialize(&mut builder)?; --- a/crates/jrsonnet-macros/src/lib.rs +++ b/crates/jrsonnet-macros/src/lib.rs @@ -13,7 +13,7 @@ LitStr, Meta, Pat, Path, PathArguments, Result, ReturnType, Token, Type, }; -use self::typed::derive_typed_inner; +use self::typed::{derive_from_untyped_inner, derive_into_untyped_inner, derive_typed_inner}; mod names; mod typed; @@ -451,6 +451,24 @@ Err(e) => e.to_compile_error().into(), } } +#[proc_macro_derive(IntoUntyped, attributes(typed))] +pub fn derive_into_untyped(item: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = parse_macro_input!(item as DeriveInput); + + match derive_into_untyped_inner(input) { + Ok(v) => v.into(), + Err(e) => e.to_compile_error().into(), + } +} +#[proc_macro_derive(FromUntyped, attributes(typed))] +pub fn derive_from_untyped(item: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = parse_macro_input!(item as DeriveInput); + + match derive_from_untyped_inner(input) { + Ok(v) => v.into(), + Err(e) => e.to_compile_error().into(), + } +} struct FormatInput { formatting: LitStr, --- a/crates/jrsonnet-macros/src/typed.rs +++ b/crates/jrsonnet-macros/src/typed.rs @@ -301,26 +301,49 @@ let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - let capacity = fields.len(); + let fields = fields + .iter() + .filter_map(TypedField::expand_field) + .collect::>(); + Ok(quote! { + const _: () = { + use ::jrsonnet_evaluator::typed::__typed_macro_prelude::*; - let typed = { - let fields = fields - .iter() - .filter_map(TypedField::expand_field) - .collect::>(); - quote! { impl #impl_generics Typed for #ident #ty_generics #where_clause { const TYPE: &'static ComplexValType = &ComplexValType::ObjectRef(&[ #(#fields,)* ]); } + }; + }) +} +pub fn derive_into_untyped_inner(input: DeriveInput) -> Result { + let syn::Data::Struct(data) = &input.data else { + return Err(Error::new(input.span(), "only structs supported")); + }; + + let ident = &input.ident; + let fields = data + .fields + .iter() + .map(TypedField::parse) + .collect::>>()?; + + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + + let capacity = fields.len(); + + let mut names = Names::default(); + + let fields_serialize = fields + .iter() + .map(|f| f.expand_serialize(&mut names)) + .collect::>(); - impl #impl_generics FromUntyped for #ident #ty_generics #where_clause { - fn from_untyped(value: Val) -> JrResult { - let obj = value.as_obj().expect("shape is correct"); - Self::parse(&obj) - } - } + let names_expanded = names.expand(); + Ok(quote! { + const _: () = { + use ::jrsonnet_evaluator::typed::__typed_macro_prelude::*; impl #impl_generics IntoUntyped for #ident #ty_generics #where_clause { fn into_untyped(value: Self) -> JrResult { @@ -329,42 +352,57 @@ Ok(Val::Obj(out.build())) } } - } + + #names_expanded + + impl #impl_generics SerializeTypedObj for #ident #ty_generics #where_clause { + fn serialize(self, out: &mut ObjValueBuilder) -> JrResult<()> { + NAMES.with(|__names| { + #(#fields_serialize)* + + Ok(()) + }) + } + } + }; + }) +} +pub fn derive_from_untyped_inner(input: DeriveInput) -> Result { + let syn::Data::Struct(data) = &input.data else { + return Err(Error::new(input.span(), "only structs supported")); }; + let ident = &input.ident; + let fields = data + .fields + .iter() + .map(TypedField::parse) + .collect::>>()?; + + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + let mut names = Names::default(); let fields_parse = fields .iter() .map(|f| f.expand_parse(&mut names)) - .collect::>(); - let fields_serialize = fields - .iter() - .map(|f| f.expand_serialize(&mut names)) .collect::>(); let names_expanded = names.expand(); Ok(quote! { const _: () = { - use ::jrsonnet_evaluator::{ - typed::{ComplexValType, Typed, IntoUntyped, FromUntyped, TypedObj, CheckType}, - Val, State, - error::{ErrorKind, Result as JrResult}, - ObjValueBuilder, ObjValue, IStr, - }; + use ::jrsonnet_evaluator::typed::__typed_macro_prelude::*; - #typed + impl #impl_generics FromUntyped for #ident #ty_generics #where_clause { + fn from_untyped(value: Val) -> JrResult { + let obj = value.as_obj().expect("shape is correct"); + Self::parse(&obj) + } + } #names_expanded - impl #impl_generics TypedObj for #ident #ty_generics #where_clause { - fn serialize(self, out: &mut ObjValueBuilder) -> JrResult<()> { - NAMES.with(|__names| { - #(#fields_serialize)* - - Ok(()) - }) - } + impl #impl_generics ParseTypedObj for #ident #ty_generics #where_clause { fn parse(obj: &ObjValue) -> JrResult { NAMES.with(|__names| Ok(Self { #(#fields_parse)* --- a/crates/jrsonnet-stdlib/src/manifest/ini.rs +++ b/crates/jrsonnet-stdlib/src/manifest/ini.rs @@ -82,7 +82,7 @@ Ok(()) } -#[derive(Typed)] +#[derive(Typed, FromUntyped)] struct IniObj { main: Option, // TODO: Preserve section order? -- gitstuff