git.delta.rocks / jrsonnet / refs/commits / f0c8338d487d

difftreelog

refactor split TypedObj derives

oqrkzkryYaroslav Bolyukin2026-03-22parent: #bd50dd1.patch.diff
in: master

5 files changed

modifiedcrates/jrsonnet-evaluator/src/arr/spec.rsdiffbeforeafterboth
--- 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<Val>,
modifiedcrates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth
--- 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<K: Trace>(PhantomData<fn() -> K>);
 impl<K> ThunkMapper<Val> for ThunkFromUntyped<K>
@@ -49,9 +61,11 @@
 	}
 }
 
-pub trait TypedObj: Typed {
-	fn serialize(self, out: &mut ObjValueBuilder) -> Result<()>;
+pub trait ParseTypedObj: Typed {
 	fn parse(obj: &ObjValue) -> Result<Self>;
+}
+pub trait SerializeTypedObj: Typed {
+	fn serialize(self, out: &mut ObjValueBuilder) -> Result<()>;
 	fn into_object(self) -> Result<ObjValue> {
 		let mut builder = ObjValueBuilder::new();
 		self.serialize(&mut builder)?;
modifiedcrates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth
--- 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,
modifiedcrates/jrsonnet-macros/src/typed.rsdiffbeforeafterboth
--- 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::<Vec<_>>();
+	Ok(quote! {
+		const _: () = {
+			use ::jrsonnet_evaluator::typed::__typed_macro_prelude::*;
 
-	let typed = {
-		let fields = fields
-			.iter()
-			.filter_map(TypedField::expand_field)
-			.collect::<Vec<_>>();
-		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<TokenStream> {
+	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::<Result<Vec<_>>>()?;
+
+	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::<Vec<_>>();
 
-			impl #impl_generics FromUntyped for #ident #ty_generics #where_clause {
-				fn from_untyped(value: Val) -> JrResult<Self> {
-					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<Val> {
@@ -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<TokenStream> {
+	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::<Result<Vec<_>>>()?;
+
+	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::<Vec<_>>();
-	let fields_serialize = fields
-		.iter()
-		.map(|f| f.expand_serialize(&mut names))
 		.collect::<Vec<_>>();
 
 	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<Self> {
+					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<Self> {
 					NAMES.with(|__names| Ok(Self {
 						#(#fields_parse)*
modifiedcrates/jrsonnet-stdlib/src/manifest/ini.rsdiffbeforeafterboth
after · crates/jrsonnet-stdlib/src/manifest/ini.rs
1use std::collections::BTreeMap;23use jrsonnet_evaluator::{4	manifest::{ManifestFormat, ToStringFormat},5	typed::{FromUntyped, Typed},6	ObjValue, Result, ResultExt, Val,7};8use jrsonnet_parser::IStr;910pub struct IniFormat {11	#[cfg(feature = "exp-preserve-order")]12	preserve_order: bool,13	final_newline: bool,14}1516impl IniFormat {17	pub fn std(#[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> Self {18		Self {19			#[cfg(feature = "exp-preserve-order")]20			preserve_order,21			final_newline: true,22		}23	}24	pub fn cli(#[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> Self {25		Self {26			#[cfg(feature = "exp-preserve-order")]27			preserve_order,28			final_newline: false,29		}30	}31}3233impl ManifestFormat for IniFormat {34	fn manifest_buf(&self, val: Val, buf: &mut String) -> Result<()> {35		manifest_ini_obj(36			self,37			IniObj::from_untyped(val).description("ini object structure")?,38			buf,39		)40	}41}4243fn manifest_ini_body(44	#[cfg(feature = "exp-preserve-order")] format: &IniFormat,45	body: ObjValue,46	out: &mut String,47) -> Result<()> {48	for (i, (key, value)) in body49		.iter(50			#[cfg(feature = "exp-preserve-order")]51			format.preserve_order,52		)53		.enumerate()54	{55		if i != 0 || !out.is_empty() {56			out.push('\n');57		}58		let value = value.with_description(|| format!("field <{key}> evaluation"))?;59		let manifest_desc = || format!("field <{key}> manifestification");60		if let Some(arr) = value.as_arr() {61			for (i, ele) in arr.iter().enumerate() {62				if i != 0 {63					out.push('\n');64				}65				let ele = ele66					.with_description(|| format!("elem <{i}> evaluation"))67					.with_description(manifest_desc)?;68				out.push_str(&key);69				out.push_str(" = ");70				ToStringFormat71					.manifest_buf(ele, out)72					.with_description(manifest_desc)?;73			}74		} else {75			out.push_str(&key);76			out.push_str(" = ");77			ToStringFormat78				.manifest_buf(value, out)79				.with_description(manifest_desc)?;80		}81	}82	Ok(())83}8485#[derive(Typed, FromUntyped)]86struct IniObj {87	main: Option<ObjValue>,88	// TODO: Preserve section order?89	sections: BTreeMap<IStr, ObjValue>,90}9192fn manifest_ini_obj(format: &IniFormat, obj: IniObj, out: &mut String) -> Result<()> {93	if let Some(main) = obj.main {94		manifest_ini_body(95			#[cfg(feature = "exp-preserve-order")]96			format,97			main,98			out,99		)100		.description("<main> manifestification")?;101	}102	for (i, (section, val)) in obj.sections.into_iter().enumerate() {103		if i != 0 || !out.is_empty() {104			out.push('\n');105		}106		out.push('[');107		out.push_str(&section);108		out.push(']');109		manifest_ini_body(110			#[cfg(feature = "exp-preserve-order")]111			format,112			val,113			out,114		)115		.with_description(|| format!("<{section}> section manifestification"))?;116	}117	if format.final_newline {118		out.push('\n');119	}120	Ok(())121}