aboutsummaryrefslogtreecommitdiff
path: root/mingling_macros/src/structural_data.rs
diff options
context:
space:
mode:
Diffstat (limited to 'mingling_macros/src/structural_data.rs')
-rw-r--r--mingling_macros/src/structural_data.rs336
1 files changed, 0 insertions, 336 deletions
diff --git a/mingling_macros/src/structural_data.rs b/mingling_macros/src/structural_data.rs
deleted file mode 100644
index 5350d7e..0000000
--- a/mingling_macros/src/structural_data.rs
+++ /dev/null
@@ -1,336 +0,0 @@
-#![allow(dead_code)]
-
-use proc_macro::TokenStream;
-use quote::quote;
-use syn::{DeriveInput, Ident, TypePath, parse_macro_input};
-
-use crate::get_global_set;
-
-/// Derive macro for `StructuralData`.
-///
-/// This marks a type as eligible for structured output (JSON / YAML / TOML / RON).
-/// The type must also implement `serde::Serialize` — the generated `impl StructuralData`
-/// will fail to compile if `Serialize` is not in scope or implemented.
-///
-/// Also registers the type name in the global `STRUCTURED_TYPES` registry so that
-/// the `structural_render` match arm is generated by `gen_program!()`.
-pub(crate) fn derive_structural_data(input: TokenStream) -> TokenStream {
- let input = parse_macro_input!(input as DeriveInput);
- let type_name = input.ident;
-
- // Register in STRUCTURED_TYPES
- let type_name_str = type_name.to_string();
- get_global_set(&crate::STRUCTURED_TYPES)
- .lock()
- .unwrap()
- .insert(type_name_str);
-
- // Generate BOTH the sealed impl AND the StructuralData impl.
- // Users cannot implement StructuralDataSealed manually (it's #[doc(hidden)]),
- // so the only way to get StructuralData is through this derive macro.
- let expanded = quote! {
- impl ::mingling::__private::StructuralDataSealed for #type_name {}
- impl ::mingling::__private::StructuralData for #type_name {}
- };
-
- expanded.into()
-}
-
-/// `pack_structural!` — like `pack!` but also marks the type as supporting
-/// structured output via `StructuralData`.
-///
-/// # Syntax
-///
-/// ```rust,ignore
-/// pack_structural!(Info = (String, i32));
-/// ```
-///
-/// This is equivalent to:
-/// ```rust,ignore
-/// pack!(Info = (String, i32));
-/// impl ::mingling::StructuralData for Info {}
-/// ```
-pub(crate) fn pack_structural(input: TokenStream) -> TokenStream {
- // Parse same input format as `pack!`
- let input_parsed = syn::parse_macro_input!(input as PackStructuralInput);
- let type_name = input_parsed.type_name;
- let inner_type = input_parsed.inner_type;
- let attrs = input_parsed.attrs;
- let program_path = crate::default_program_path();
-
- // Register in STRUCTURED_TYPES
- let type_name_str = type_name.to_string();
- get_global_set(&crate::STRUCTURED_TYPES)
- .lock()
- .unwrap()
- .insert(type_name_str);
-
- // Struct definition (with Serialize derive, same as pack! under structural_renderer)
- #[cfg(not(feature = "structural_renderer"))]
- let struct_def = quote! {
- #(#attrs)*
- pub struct #type_name {
- pub inner: #inner_type,
- }
- };
-
- #[cfg(feature = "structural_renderer")]
- let struct_def = quote! {
- #(#attrs)*
- #[derive(serde::Serialize)]
- pub struct #type_name {
- pub inner: #inner_type,
- }
- };
-
- // Helper impls (same as pack!)
- let new_impl = quote! {
- impl #type_name {
- pub fn new(inner: #inner_type) -> Self {
- Self { inner }
- }
- }
- };
-
- let from_into_impl = quote! {
- impl From<#inner_type> for #type_name {
- fn from(inner: #inner_type) -> Self {
- Self::new(inner)
- }
- }
- impl From<#type_name> for #inner_type {
- fn from(wrapper: #type_name) -> #inner_type {
- wrapper.inner
- }
- }
- };
-
- let as_ref_impl = quote! {
- impl ::std::convert::AsRef<#inner_type> for #type_name {
- fn as_ref(&self) -> &#inner_type {
- &self.inner
- }
- }
- impl ::std::convert::AsMut<#inner_type> for #type_name {
- fn as_mut(&mut self) -> &mut #inner_type {
- &mut self.inner
- }
- }
- };
-
- let deref_impl = quote! {
- impl ::std::ops::Deref for #type_name {
- type Target = #inner_type;
- fn deref(&self) -> &Self::Target {
- &self.inner
- }
- }
- impl ::std::ops::DerefMut for #type_name {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.inner
- }
- }
- };
-
- let default_impl = quote! {
- impl ::std::default::Default for #type_name
- where
- #inner_type: ::std::default::Default,
- {
- fn default() -> Self {
- Self::new(::std::default::Default::default())
- }
- }
- };
-
- let register_impl = quote! {
- ::mingling::macros::register_type!(#type_name);
- };
-
- // StructuralData impl + sealed + registration
- let structural_impl = quote! {
- impl ::mingling::__private::StructuralDataSealed for #type_name {}
- impl ::mingling::__private::StructuralData for #type_name {}
- };
-
- let expanded = quote! {
- #struct_def
-
- #new_impl
- #from_into_impl
- #as_ref_impl
- #deref_impl
- #default_impl
- #register_impl
- #structural_impl
-
- impl Into<::mingling::AnyOutput<#program_path>> for #type_name {
- fn into(self) -> ::mingling::AnyOutput<#program_path> {
- ::mingling::AnyOutput::new(self)
- }
- }
-
- impl Into<::mingling::ChainProcess<#program_path>> for #type_name {
- fn into(self) -> ::mingling::ChainProcess<#program_path> {
- ::mingling::AnyOutput::new(self).route_chain()
- }
- }
-
- impl ::mingling::Groupped<#program_path> for #type_name {
- fn member_id() -> #program_path {
- #program_path::#type_name
- }
- }
- };
-
- expanded.into()
-}
-
-/// Input for `pack_structural!` — same format as `pack!`.
-struct PackStructuralInput {
- attrs: Vec<syn::Attribute>,
- type_name: Ident,
- inner_type: syn::Type,
-}
-
-impl syn::parse::Parse for PackStructuralInput {
- fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
- let attrs = input.call(syn::Attribute::parse_outer)?;
- let type_name: Ident = input.parse()?;
- input.parse::<syn::Token![=]>()?;
- let inner_type: syn::Type = input.parse()?;
- Ok(PackStructuralInput {
- attrs,
- type_name,
- inner_type,
- })
- }
-}
-
-/// `group_structural!` — like `group!` but also marks the type as supporting
-/// structured output via `StructuralData`.
-///
-/// # Syntax
-///
-/// ```rust,ignore
-/// group_structural!(Info = (String, i32));
-/// ```
-///
-/// This is equivalent to:
-/// ```rust,ignore
-/// group!(Info = (String, i32));
-/// impl ::mingling::StructuralData for Info {}
-/// ```
-pub(crate) fn group_structural(input: TokenStream) -> TokenStream {
- // Parse the same input as group!
- let input_parsed = syn::parse_macro_input!(input as GroupStructuralInput);
-
- let is_aliased = matches!(&input_parsed, GroupStructuralInput::Aliased { .. });
-
- let (type_path, type_name, alias_stmt) = match &input_parsed {
- GroupStructuralInput::Plain(type_path) => {
- let name = type_path
- .path
- .segments
- .last()
- .expect("TypePath must have at least one segment")
- .ident
- .clone();
- (type_path.clone(), name, quote! {})
- }
- GroupStructuralInput::Aliased { alias, type_path } => {
- let alias_stmt = quote! {
- pub(crate) type #alias = #type_path;
- };
- (type_path.clone(), alias.clone(), alias_stmt)
- }
- };
-
- let type_name_str = type_name.to_string();
-
- // Register in STRUCTURED_TYPES
- get_global_set(&crate::STRUCTURED_TYPES)
- .lock()
- .unwrap()
- .insert(type_name_str);
-
- let program_path = crate::default_program_path();
-
- // Generate unique module name
- let segments: Vec<String> = type_path
- .path
- .segments
- .iter()
- .map(|seg| seg.ident.to_string().to_lowercase())
- .collect();
- let module_name = Ident::new(
- &format!("internal_group_{}", segments.join("_")),
- proc_macro2::Span::call_site(),
- );
-
- // Generate the appropriate `use` statement for the original type
- // (consistent with gen_type_use in group_impl.rs)
- let type_use = if type_path.path.segments.len() > 1 {
- quote! { #[allow(unused_imports)] use #type_path; }
- } else {
- let ident = type_path
- .path
- .segments
- .last()
- .expect("TypePath must have at least one segment")
- .ident
- .clone();
- quote! { #[allow(unused_imports)] use super::#ident; }
- };
-
- let alias_use = if is_aliased {
- quote! { use super::#type_name; }
- } else {
- quote! {}
- };
-
- let expanded = quote! {
- #alias_stmt
- #[allow(non_camel_case_types)]
- mod #module_name {
- use #program_path as __MinglingProgram;
- #type_use
- #alias_use
-
- impl ::mingling::Groupped<__MinglingProgram> for #type_name {
- fn member_id() -> __MinglingProgram {
- __MinglingProgram::#type_name
- }
- }
-
- impl ::mingling::__private::StructuralDataSealed for #type_name {}
- impl ::mingling::__private::StructuralData for #type_name {}
-
- ::mingling::macros::register_type!(#type_name);
- }
- };
-
- expanded.into()
-}
-
-/// Input for `group_structural!` — same format as `group!`.
-enum GroupStructuralInput {
- Plain(TypePath),
- Aliased { alias: Ident, type_path: TypePath },
-}
-
-impl syn::parse::Parse for GroupStructuralInput {
- fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
- let fork = input.fork();
- let _first: Ident = fork.parse()?;
- if fork.peek(syn::Token![=]) {
- let alias: Ident = input.parse()?;
- let _eq: syn::Token![=] = input.parse()?;
- let type_path: TypePath = input.parse()?;
- Ok(GroupStructuralInput::Aliased { alias, type_path })
- } else {
- let type_path: TypePath = input.parse()?;
- Ok(GroupStructuralInput::Plain(type_path))
- }
- }
-}