diff options
Diffstat (limited to 'mingling_macros/src/structural_data.rs')
| -rw-r--r-- | mingling_macros/src/structural_data.rs | 336 |
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)) - } - } -} |
