From 1cbe1b0996782a46ea47c24a57b0f4d4b8c0ca06 Mon Sep 17 00:00:00 2001 From: 4dawit Date: Thu, 8 Jan 2026 19:19:03 -0600 Subject: [PATCH 1/6] feat: parse optional flat enums --- macros/src/lib.rs | 7 +- macros/src/types/enum.rs | 80 +++-- macros/src/types/named.rs | 323 +++++++++--------- macros/src/types/type_as.rs | 4 + macros/src/types/type_override.rs | 4 + macros/src/types/unit.rs | 8 + ts-rs/src/lib.rs | 14 + ts-rs/tests/integration/main.rs | 1 + .../integration/optional_flattened_enum.rs | 193 +++++++++++ 9 files changed, 440 insertions(+), 194 deletions(-) create mode 100644 ts-rs/tests/integration/optional_flattened_enum.rs diff --git a/macros/src/lib.rs b/macros/src/lib.rs index b54e588e..cf0753ee 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -91,6 +91,7 @@ impl DerivedTS { let decl = self.generate_decl_fn(&rust_ty, &generics); let dependencies = &self.dependencies; let generics_fn = self.generate_generics_fn(&generics); + let is_enum = self.is_enum; quote! { #[automatically_derived] @@ -518,7 +519,11 @@ fn entry(input: proc_macro::TokenStream) -> Result { let input = syn::parse::(input)?; let (ts, ident, generics) = match input { Item::Struct(s) => (types::struct_def(&s)?, s.ident, s.generics), - Item::Enum(e) => (types::enum_def(&e)?, e.ident, e.generics), + Item::Enum(e) => { + let mut item_type = types::enum_def(&e)?; + item_type.is_enum = true; + (item_type, e.ident, e.generics) + } _ => syn_err!(input.span(); "unsupported item"), }; diff --git a/macros/src/types/enum.rs b/macros/src/types/enum.rs index 2366e886..efc04030 100644 --- a/macros/src/types/enum.rs +++ b/macros/src/types/enum.rs @@ -35,14 +35,17 @@ pub(crate) fn r#enum_def(s: &ItemEnum) -> syn::Result { } let mut formatted_variants = Vec::new(); + let mut formatted_none = Vec::new(); let mut dependencies = Dependencies::new(crate_rename.clone()); - for variant in &s.variants { + for (index, variant) in s.variants.iter().enumerate() { format_variant( &mut formatted_variants, + &mut formatted_none, &mut dependencies, &enum_attr, variant, + index, )?; } @@ -54,9 +57,17 @@ pub(crate) fn r#enum_def(s: &ItemEnum) -> syn::Result { Ok(DerivedTS { crate_rename, inline: quote!([#(#formatted_variants),*].join(#separator)), - inline_flattened: enum_attr.repr.is_none().then_some(quote!( - format!("({})", [#(#formatted_variants),*].join(" | ")) - )), + inline_flattened: enum_attr.repr.is_none().then_some({ + if formatted_none.is_empty() { + quote!(format!("({})", [#(#formatted_variants),*].join(" | "))) + } else { + quote!(format!( + "({} | {})", + [#(#formatted_variants),*].join(" | "), + [#(#formatted_none),*].join(" | ") + )) + } + }), dependencies, docs: enum_attr.docs, export: enum_attr.export, @@ -71,9 +82,11 @@ pub(crate) fn r#enum_def(s: &ItemEnum) -> syn::Result { fn format_variant( formatted_variants: &mut Vec, + formatted_optional_variants: &mut Vec, dependencies: &mut Dependencies, enum_attr: &EnumAttr, variant: &Variant, + _index: usize, ) -> syn::Result<()> { let crate_rename = enum_attr.crate_rename(); @@ -138,23 +151,27 @@ fn format_variant( } }; - let formatted = match (untagged_variant, enum_attr.tagged()?) { - (true, _) | (_, Tagged::Untagged) => quote!(#parsed_ty), + let never_ty = quote!(format!("\"{}\"?: never", #ts_name)); + let (formatted, formatted_optional) = match (untagged_variant, enum_attr.tagged()?) { + (true, _) | (_, Tagged::Untagged) => (quote!(#parsed_ty), None), (false, Tagged::Externally) => match &variant.fields { - Fields::Unit => quote!(format!("\"{}\"", #ts_name)), + Fields::Unit => (quote!(format!("\"{}\"", #ts_name)), None), Fields::Unnamed(unnamed) if unnamed.unnamed.len() == 1 => { let field = &unnamed.unnamed[0]; - let field_attr = FieldAttr::from_attrs(&field.attrs)?; + let field_attr = FieldAttr::from_attrs(&unnamed.unnamed[0].attrs)?; field_attr.assert_validity(field)?; if field_attr.skip { - quote!(format!("\"{}\"", #ts_name)) + (quote!(format!("\"{}\"", #ts_name)), None) } else { - quote!(format!("{{ \"{}\": {} }}", #ts_name, #parsed_ty)) + ( + quote!(format!("{{ \"{}\": {} }}", #ts_name, #parsed_ty)), + Some(quote!(format!("{{ \"{}\": {}; {} }}", #ts_name, #parsed_ty, #never_ty))) + ) } } - _ => quote!(format!("{{ \"{}\": {} }}", #ts_name, #parsed_ty)), + _ => (quote!(format!("{{ \"{}\": {} }}", #ts_name, #parsed_ty)), None), }, (false, Tagged::Adjacently { tag, content }) => match &variant.fields { Fields::Unnamed(unnamed) if unnamed.unnamed.len() == 1 => { @@ -163,29 +180,26 @@ fn format_variant( field_attr.assert_validity(field)?; + let field_ty = field_attr.type_as(&field.ty); + if field_attr.skip { - quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #ts_name)) + (quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #ts_name)), None) } else { let ty = match field_attr.type_override { Some(type_override) => quote!(#type_override), - None => { - let ty = field_attr.type_as(&field.ty); - quote!(<#ty as #crate_rename::TS>::name(cfg)) - } + None => quote!(<#field_ty as #crate_rename::TS>::name(cfg)) }; - quote!( - format!("{{ \"{}\": \"{}\", \"{}\": {} }}", #tag, #ts_name, #content, #ty) - ) + + (quote!(format!("{{ \"{}\": \"{}\", \"{}\": {} }}", #tag, #ts_name, #content, #ty)), None) } } - Fields::Unit => quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #ts_name)), - _ => quote!( - format!("{{ \"{}\": \"{}\", \"{}\": {} }}", #tag, #ts_name, #content, #parsed_ty) - ), + Fields::Unit => (quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #ts_name)), None), + _ => (quote!(format!("{{ \"{}\": \"{}\", \"{}\": {} }}", #tag, #ts_name, #content, #parsed_ty)), None) + }, (false, Tagged::Internally { tag }) => match variant_type.inline_flattened { Some(_) => { - quote! { #parsed_ty } + (quote! { #parsed_ty }, None) } None => match &variant.fields { Fields::Unnamed(unnamed) if unnamed.unnamed.len() == 1 => { @@ -194,29 +208,31 @@ fn format_variant( field_attr.assert_validity(field)?; + let field_ty = field_attr.type_as(&field.ty); + if field_attr.skip { - quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #ts_name)) + (quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #ts_name)), None) } else { let ty = match field_attr.type_override { Some(type_override) => quote! { #type_override }, None => { - let ty = field_attr.type_as(&field.ty); - quote!(<#ty as #crate_rename::TS>::name(cfg)) + quote!(<#field_ty as #crate_rename::TS>::name(cfg)) } }; - quote!(format!("{{ \"{}\": \"{}\" }} & {}", #tag, #ts_name, #ty)) + (quote!(format!("{{ \"{}\": \"{}\" }} & {}", #tag, #ts_name, #ty)), None) } } - Fields::Unit => quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #ts_name)), - _ => { - quote!(format!("{{ \"{}\": \"{}\" }} & {}", #tag, #ts_name, #parsed_ty)) - } + Fields::Unit => (quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #ts_name)), None), + _ => (quote!(format!("{{ \"{}\": \"{}\" }} & {}", #tag, #ts_name, #parsed_ty)), None) }, }, }; formatted_variants.push(formatted); + if let Some(formatted) = formatted_optional { + formatted_optional_variants.push(formatted); + } Ok(()) } diff --git a/macros/src/types/named.rs b/macros/src/types/named.rs index fbbb66f3..f1f95942 100644 --- a/macros/src/types/named.rs +++ b/macros/src/types/named.rs @@ -1,161 +1,162 @@ -use proc_macro2::TokenStream; -use quote::quote; -use syn::{spanned::Spanned, Expr, Field, FieldsNamed, Path, Result}; - -use crate::{ - attr::{Attr, ContainerAttr, FieldAttr, Inflection, StructAttr}, - deps::Dependencies, - optional::Optional, - utils::{raw_name_to_ts_field, to_ts_ident}, - DerivedTS, -}; - -pub(crate) fn named(attr: &StructAttr, ts_name: Expr, fields: &FieldsNamed) -> Result { - let crate_rename = attr.crate_rename(); - - let mut formatted_fields = Vec::new(); - let mut flattened_fields = Vec::new(); - let mut dependencies = Dependencies::new(crate_rename.clone()); - - if let Some(tag) = &attr.tag { - formatted_fields.push(quote! { - format!("\"{}\": \"{}\",", #tag, #ts_name) - }); - } - - for field in &fields.named { - format_field( - &crate_rename, - &mut formatted_fields, - &mut flattened_fields, - &mut dependencies, - field, - &attr.rename_all, - attr.optional_fields, - )?; - } - - let fields = quote!(<[String]>::join(&[#(#formatted_fields),*], " ")); - let flattened = quote!(<[String]>::join(&[#(#flattened_fields),*], " & ")); - - let inline = match (formatted_fields.len(), flattened_fields.len()) { - (0, 0) => quote!("{ }".to_owned()), - (_, 0) => quote!(format!("{{ {} }}", #fields)), - (0, 1) => quote! {{ - if #flattened.starts_with('(') && #flattened.ends_with(')') { - #flattened[1..#flattened.len() - 1].trim().to_owned() - } else { - #flattened.trim().to_owned() - } - }}, - (0, _) => quote!(#flattened), - (_, _) => quote!(format!("{{ {} }} & {}", #fields, #flattened)), - }; - - let inline_flattened = match (formatted_fields.len(), flattened_fields.len()) { - (0, 0) => quote!("{ }".to_owned()), - (_, 0) => quote!(format!("{{ {} }}", #fields)), - (0, _) => quote!(#flattened), - (_, _) => quote!(format!("{{ {} }} & {}", #fields, #flattened)), - }; - - Ok(DerivedTS { - crate_rename, - // the `replace` combines `{ ... } & { ... }` into just one `{ ... }`. Not necessary, but it - // results in simpler type definitions. - inline: quote!(#inline.replace(" } & { ", " ")), - inline_flattened: Some(quote!(#inline_flattened.replace(" } & { ", " "))), - docs: attr.docs.clone(), - dependencies, - export: attr.export, - export_to: attr.export_to.clone(), - ts_name, - concrete: attr.concrete.clone(), - bound: attr.bound.clone(), - ts_enum: None, - is_enum: quote!(false), - }) -} - -// build an expression which expands to a string, representing a single field of a struct. -// -// formatted_fields will contain all the fields that do not contain the flatten -// attribute, in the format -// key: type, -// -// flattened_fields will contain all the fields that contain the flatten attribute -// in their respective formats, which for a named struct is the same as formatted_fields, -// but for enums is -// ({ /* variant data */ } | { /* variant data */ }) -fn format_field( - crate_rename: &Path, - formatted_fields: &mut Vec, - flattened_fields: &mut Vec, - dependencies: &mut Dependencies, - field: &Field, - rename_all: &Option, - struct_optional: Optional, -) -> Result<()> { - let field_attr = FieldAttr::from_attrs(&field.attrs)?; - - field_attr.assert_validity(field)?; - - if field_attr.skip { - return Ok(()); - } - - let ty = field_attr.type_as(&field.ty); - - let (is_optional, ty) = crate::optional::apply( - crate_rename, - struct_optional, - &ty, - &field_attr, - field.span(), - ); - let optional_annotation = quote!(if #is_optional { "?" } else { "" }); - - if field_attr.type_override.is_none() { - if field_attr.inline || field_attr.flatten { - dependencies.append_from(&ty); - } else { - dependencies.push(&ty); - } - } - - if field_attr.flatten { - flattened_fields.push(quote!(<#ty as #crate_rename::TS>::inline_flattened(cfg))); - return Ok(()); - } - - let formatted_ty = field_attr - .type_override - .map(|t| quote!(#t)) - .unwrap_or_else(|| { - if field_attr.inline { - quote!(<#ty as #crate_rename::TS>::inline(cfg)) - } else { - quote!(<#ty as #crate_rename::TS>::name(cfg)) - } - }); - - let field_name = to_ts_ident(field.ident.as_ref().unwrap()); - let name = match (field_attr.rename, rename_all) { - (Some(rn), _) => rn, - (None, Some(rn)) => rn.apply(&field_name), - (None, None) => field_name, - }; - let valid_name = raw_name_to_ts_field(name); - - // Start every doc string with a newline, because when other characters are in front, it is not "understood" by VSCode - let docs = match &*field_attr.docs { - &[] => quote!(""), - docs => quote!(format!("\n{}", #crate_rename::format_docs(&[#(#docs),*]))), - }; - - formatted_fields.push(quote! { - format!("{}{}{}: {},", #docs, #valid_name, #optional_annotation, #formatted_ty) - }); - - Ok(()) -} +use proc_macro2::TokenStream; +use quote::quote; +use syn::{spanned::Spanned, Expr, Field, FieldsNamed, Path, Result}; + +use crate::{ + attr::{Attr, ContainerAttr, FieldAttr, Inflection, StructAttr}, + deps::Dependencies, + optional::Optional, + utils::{raw_name_to_ts_field, to_ts_ident}, + DerivedTS, +}; + +pub(crate) fn named(attr: &StructAttr, ts_name: Expr, fields: &FieldsNamed) -> Result { + let crate_rename = attr.crate_rename(); + + let mut formatted_fields = Vec::new(); + let mut flattened_fields = Vec::new(); + let mut dependencies = Dependencies::new(crate_rename.clone()); + + if let Some(tag) = &attr.tag { + formatted_fields.push(quote! { + format!("\"{}\": \"{}\",", #tag, #ts_name) + }); + } + + for field in &fields.named { + format_field( + &crate_rename, + &mut formatted_fields, + &mut flattened_fields, + &mut dependencies, + field, + &attr.rename_all, + attr.optional_fields, + )?; + } + + let fields = quote!(<[String]>::join(&[#(#formatted_fields),*], " ")); + let flattened = quote!(<[String]>::join(&[#(#flattened_fields),*], " & ")); + + let inline = match (formatted_fields.len(), flattened_fields.len()) { + (0, 0) => quote!("{ }".to_owned()), + (_, 0) => quote!(format!("{{ {} }}", #fields)), + (0, 1) => quote! {{ + if #flattened.starts_with('(') && #flattened.ends_with(')') { + #flattened[1..#flattened.len() - 1].trim().to_owned() + } else { + #flattened.trim().to_owned() + } + }}, + (0, _) => quote!(#flattened), + (_, _) => quote!(format!("{{ {} }} & {}", #fields, #flattened)), + }; + + let inline_flattened = match (formatted_fields.len(), flattened_fields.len()) { + (0, 0) => quote!("{ }".to_owned()), + (_, 0) => quote!(format!("{{ {} }}", #fields)), + (0, _) => quote!(#flattened), + (_, _) => quote!(format!("{{ {} }} & {}", #fields, #flattened)), + }; + + Ok(DerivedTS { + crate_rename, + // the `replace` combines `{ ... } & { ... }` into just one `{ ... }`. Not necessary, but it + // results in simpler type definitions. + inline: quote!(#inline.replace(" } & { ", " ")), + inline_flattened: Some(quote!(#inline_flattened.replace(" } & { ", " "))), + optional_inline_flattened: Some(quote!(#inline_flattened.replace(" } & { ", " "))), + docs: attr.docs.clone(), + dependencies, + export: attr.export, + export_to: attr.export_to.clone(), + ts_name, + concrete: attr.concrete.clone(), + bound: attr.bound.clone(), + ts_enum: None, + is_enum: quote!(false), + }) +} + +// build an expression which expands to a string, representing a single field of a struct. +// +// formatted_fields will contain all the fields that do not contain the flatten +// attribute, in the format +// key: type, +// +// flattened_fields will contain all the fields that contain the flatten attribute +// in their respective formats, which for a named struct is the same as formatted_fields, +// but for enums is +// ({ /* variant data */ } | { /* variant data */ }) +fn format_field( + crate_rename: &Path, + formatted_fields: &mut Vec, + flattened_fields: &mut Vec, + dependencies: &mut Dependencies, + field: &Field, + rename_all: &Option, + struct_optional: Optional, +) -> Result<()> { + let field_attr = FieldAttr::from_attrs(&field.attrs)?; + + field_attr.assert_validity(field)?; + + if field_attr.skip { + return Ok(()); + } + + let ty = field_attr.type_as(&field.ty); + + let (is_optional, ty) = crate::optional::apply( + crate_rename, + struct_optional, + &ty, + &field_attr, + field.span(), + ); + let optional_annotation = quote!(if #is_optional { "?" } else { "" }); + + if field_attr.type_override.is_none() { + if field_attr.inline || field_attr.flatten { + dependencies.append_from(&ty); + } else { + dependencies.push(&ty); + } + } + + if field_attr.flatten { + flattened_fields.push(quote!(<#ty as #crate_rename::TS>::inline_flattened(cfg))); + return Ok(()); + } + + let formatted_ty = field_attr + .type_override + .map(|t| quote!(#t)) + .unwrap_or_else(|| { + if field_attr.inline { + quote!(<#ty as #crate_rename::TS>::inline(cfg)) + } else { + quote!(<#ty as #crate_rename::TS>::name(cfg)) + } + }); + + let field_name = to_ts_ident(field.ident.as_ref().unwrap()); + let name = match (field_attr.rename, rename_all) { + (Some(rn), _) => rn, + (None, Some(rn)) => rn.apply(&field_name), + (None, None) => field_name, + }; + let valid_name = raw_name_to_ts_field(name); + + // Start every doc string with a newline, because when other characters are in front, it is not "understood" by VSCode + let docs = match &*field_attr.docs { + &[] => quote!(""), + docs => quote!(format!("\n{}", #crate_rename::format_docs(&[#(#docs),*]))), + }; + + formatted_fields.push(quote! { + format!("{}{}{}: {},", #docs, #valid_name, #optional_annotation, #formatted_ty) + }); + + Ok(()) +} diff --git a/macros/src/types/type_as.rs b/macros/src/types/type_as.rs index 44d31793..27f98b0e 100644 --- a/macros/src/types/type_as.rs +++ b/macros/src/types/type_as.rs @@ -51,6 +51,10 @@ pub(crate) fn type_as_enum(attr: &EnumAttr, ts_name: Expr, type_as: &Type) -> Re concrete: attr.concrete.clone(), bound: attr.bound.clone(), ts_enum: None, +<<<<<<< HEAD is_enum: quote!(<#type_as as #crate_rename::TS>::IS_ENUM), +======= + is_enum: false, +>>>>>>> a316b00 (feat: parse optional flat enums) }) } diff --git a/macros/src/types/type_override.rs b/macros/src/types/type_override.rs index 00b4a553..653ed1df 100644 --- a/macros/src/types/type_override.rs +++ b/macros/src/types/type_override.rs @@ -49,6 +49,10 @@ pub(crate) fn type_override_enum( concrete: attr.concrete.clone(), bound: attr.bound.clone(), ts_enum: None, +<<<<<<< HEAD is_enum: quote!(true), // we dont know what the override is, so we preserve is_enum +======= + is_enum: false, +>>>>>>> a316b00 (feat: parse optional flat enums) }) } diff --git a/macros/src/types/unit.rs b/macros/src/types/unit.rs index 2a95b502..e2a6cb29 100644 --- a/macros/src/types/unit.rs +++ b/macros/src/types/unit.rs @@ -41,7 +41,11 @@ pub(crate) fn empty_array(attr: &StructAttr, ts_name: Expr) -> DerivedTS { concrete: attr.concrete.clone(), bound: attr.bound.clone(), ts_enum: None, +<<<<<<< HEAD is_enum: quote!(false), +======= + is_enum: false, +>>>>>>> a316b00 (feat: parse optional flat enums) } } @@ -60,6 +64,10 @@ pub(crate) fn null(attr: &StructAttr, ts_name: Expr) -> DerivedTS { concrete: attr.concrete.clone(), bound: attr.bound.clone(), ts_enum: None, +<<<<<<< HEAD is_enum: quote!(false), +======= + is_enum: false, +>>>>>>> a316b00 (feat: parse optional flat enums) } } diff --git a/ts-rs/src/lib.rs b/ts-rs/src/lib.rs index 524296ff..4e8539e9 100644 --- a/ts-rs/src/lib.rs +++ b/ts-rs/src/lib.rs @@ -389,6 +389,8 @@ pub trait TS { #[doc(hidden)] const IS_OPTION: bool = false; + #[doc(hidden)] + const IS_ENUM: bool = false; #[doc(hidden)] const IS_ENUM: bool = false; @@ -850,6 +852,18 @@ impl TS for Option { format!("{} | null", T::inline(cfg)) } + fn inline_flattened(cfg: &Config) -> String { + if ::IS_ENUM { + ::inline_flattened(cfg) + } else { + panic!("{} cannot be flattened", ::name(cfg)) + } + } + + fn optional_inline_flattened(cfg: &Config) -> String { + ::optional_inline_flattened(cfg) + } + fn visit_dependencies(v: &mut impl TypeVisitor) where Self: 'static, diff --git a/ts-rs/tests/integration/main.rs b/ts-rs/tests/integration/main.rs index d2c85252..9636c81c 100644 --- a/ts-rs/tests/integration/main.rs +++ b/ts-rs/tests/integration/main.rs @@ -46,6 +46,7 @@ mod list; mod merge_same_file_imports; mod nested; mod optional_field; +mod optional_flattened_enum; mod path_bug; mod ranges; mod raw_idents; diff --git a/ts-rs/tests/integration/optional_flattened_enum.rs b/ts-rs/tests/integration/optional_flattened_enum.rs new file mode 100644 index 00000000..7051fa3f --- /dev/null +++ b/ts-rs/tests/integration/optional_flattened_enum.rs @@ -0,0 +1,193 @@ +use serde::Serialize; +use ts_rs::TS; + +#[test] +fn optional_flatten_enum_adds_empty_object() { + #[derive(TS, Serialize)] + #[serde(rename_all = "camelCase")] + enum Enum { + FirstOption(String), + SecondOption(bool), + } + + #[derive(TS, Serialize)] + struct T { + a: String, + #[serde(flatten)] + flattened: Option, + } + + assert_eq!( + T::inline_flattened(), + r#"{ a: string, } & ({ "firstOption": string; "secondOption"?: never } | { "secondOption": boolean; "firstOption"?: never } | { "firstOption"?: never; "secondOption"?: never })"# + ); +} + +#[test] +fn optional_flatten_unit_variants_adds_empty_object() { + #[derive(TS, Serialize)] + #[serde(rename_all = "camelCase")] + enum Enum { + First, + Second, + Third, + } + + #[derive(TS, Serialize)] + struct T { + a: String, + #[serde(flatten)] + status: Option, + } + + assert_eq!( + T::inline_flattened(), + r#"{ a: string, } & ("first" | "second" | "third" | { "first"?: never; "second"?: never; "third"?: never })"# + ); +} + +#[test] +fn optional_flatten_mixed_variants_adds_empty_object() { + #[derive(TS, Serialize)] + #[serde(rename_all = "camelCase")] + enum Enum { + Unit, + Tuple(i32, String), + Struct { x: i32, y: String }, + } + + #[derive(TS, Serialize)] + struct T { + a: String, + #[serde(flatten)] + data: Option, + } + + assert_eq!( + T::inline_flattened(), + r#"{ a: string, } & ("unit" | { "tuple": [number, string]; "struct"?: never; "unit"?: never } | { "struct": { x: number, y: string, }; "tuple"?: never; "unit"?: never } | { "unit"?: never; "tuple"?: never; "struct"?: never })"# + ); +} + +#[test] +fn optional_flatten_with_nested_objects_adds_empty_object() { + #[derive(TS, Serialize)] + struct Inner { + value: i32, + } + + #[derive(TS, Serialize)] + #[serde(rename_all = "camelCase")] + enum Enum { + First(Inner), + Second(Inner), + } + + #[derive(TS, Serialize)] + struct T { + a: String, + #[serde(flatten)] + nested: Option, + } + + assert_eq!( + T::inline_flattened(), + r#"{ a: string, } & ({ "first": Inner; "second"?: never } | { "second": Inner; "first"?: never } | { "first"?: never; "second"?: never })"# + ); +} + +#[test] +fn multiple_optional_flattened_enums_each_add_empty_object() { + #[derive(TS, Serialize)] + #[serde(rename_all = "camelCase")] + enum EnumA { + OptionA(String), + } + + #[derive(TS, Serialize)] + #[serde(rename_all = "camelCase")] + enum EnumB { + OptionB(i32), + } + + #[derive(TS, Serialize)] + struct T { + a: String, + #[serde(flatten)] + a_enum: Option, + #[serde(flatten)] + b_enum: Option, + } + + let result = T::inline_flattened(); + + assert!(result.contains(r#"{ "optionA": string; "optionA"?: never } | { "optionA"?: never }"#)); + assert!(result.contains(r#"{ "optionB": number; "optionB"?: never } | { "optionB"?: never }"#)); +} + +#[test] +fn optional_flatten_with_rename_all_kebab_case() { + #[derive(TS, Serialize)] + #[serde(rename_all = "kebab-case")] + enum Enum { + FirstOption(String), + SecondOption(bool), + } + + #[derive(TS, Serialize)] + struct T { + a: String, + #[serde(flatten)] + flattened: Option, + } + + let result = T::inline_flattened(); + + assert!(result.contains(r#""first-option": string"#)); + assert!(result.contains(r#""second-option": boolean"#)); + assert!(result.contains(r#""first-option"?: never"#)); + assert!(result.contains(r#""second-option"?: never"#)); +} + +#[test] +fn optional_flatten_single_variant_adds_empty_object() { + #[derive(TS, Serialize)] + #[serde(rename_all = "camelCase")] + enum Enum { + Only(String), + } + + #[derive(TS, Serialize)] + struct T { + a: String, + #[serde(flatten)] + single: Option, + } + + assert_eq!( + T::inline_flattened(), + r#"{ a: string, } & ({ "only": string } | { "only"?: never })"# + ); +} + +#[test] +fn non_optional_flatten_enum_is_unchanged() { + #[derive(TS, Serialize)] + #[serde(rename_all = "camelCase")] + enum Enum { + FirstOption(String), + SecondOption(bool), + } + + #[derive(TS, Serialize)] + struct T { + a: String, + #[serde(flatten)] + flattened: Enum, + } + + assert_eq!( + T::inline_flattened(), + r#"{ a: string, } & ({ "firstOption": string } | { "secondOption": boolean })"# + ); +} From 31cc9315029b6ec7aa52f4e21077753ff54656d2 Mon Sep 17 00:00:00 2001 From: 4dawit Date: Fri, 9 Jan 2026 13:59:46 -0600 Subject: [PATCH 2/6] fix: add optional_flattened_enum to still allow normal flat enum parsing --- macros/src/lib.rs | 13 +++ macros/src/types/enum.rs | 88 +++++++++++++------ macros/src/types/newtype.rs | 1 + macros/src/types/tuple.rs | 1 + macros/src/types/type_as.rs | 6 +- macros/src/types/type_override.rs | 6 +- macros/src/types/unit.rs | 11 +-- ts-rs/src/lib.rs | 21 +++++ ts-rs/tests/integration/issue_308.rs | 1 + .../integration/optional_flattened_enum.rs | 16 ++-- 10 files changed, 115 insertions(+), 49 deletions(-) diff --git a/macros/src/lib.rs b/macros/src/lib.rs index cf0753ee..815b6f36 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -26,6 +26,7 @@ struct DerivedTS { docs: Vec, inline: TokenStream, inline_flattened: Option, + optional_inline_flattened: Option, dependencies: Dependencies, concrete: HashMap, bound: Option>, @@ -180,6 +181,7 @@ impl DerivedTS { fn name(cfg: &#crate_rename::Config) -> String { stringify!(#generics).to_owned() } fn inline(cfg: &#crate_rename::Config) -> String { panic!("{} cannot be inlined", #name) } fn inline_flattened(cfg: &#crate_rename::Config) -> String { stringify!(#generics).to_owned() } + fn optional_inline_flattened(cfg: &#crate_rename::Config) -> String { stringify!(#generics).to_owned() } fn decl(cfg: &#crate_rename::Config) -> String { panic!("{} cannot be declared", #name) } fn decl_concrete(cfg: &#crate_rename::Config) -> String { panic!("{} cannot be declared", #name) } } @@ -252,6 +254,13 @@ impl DerivedTS { } }); + let optional_inline_flattened = + self.optional_inline_flattened.clone().unwrap_or_else(|| { + quote! { + panic!("{} cannot be flattened", ::name()) + } + }); + let inline = match self.ts_enum { Some(Repr::Int) => quote! { let variants = #inline.replace(|x: char| !x.is_numeric() && x != ',', ""); @@ -306,6 +315,10 @@ impl DerivedTS { fn inline_flattened(cfg: &#crate_rename::Config) -> String { #inline_flattened } + + fn optional_inline_flattened() -> String { + #optional_inline_flattened + } } } diff --git a/macros/src/types/enum.rs b/macros/src/types/enum.rs index efc04030..c90759fd 100644 --- a/macros/src/types/enum.rs +++ b/macros/src/types/enum.rs @@ -35,13 +35,15 @@ pub(crate) fn r#enum_def(s: &ItemEnum) -> syn::Result { } let mut formatted_variants = Vec::new(); - let mut formatted_none = Vec::new(); + let mut formatted_optional_variants = Vec::new(); + let mut formatted_never = Vec::new(); let mut dependencies = Dependencies::new(crate_rename.clone()); for (index, variant) in s.variants.iter().enumerate() { format_variant( &mut formatted_variants, - &mut formatted_none, + &mut formatted_optional_variants, + &mut formatted_never, &mut dependencies, &enum_attr, variant, @@ -57,17 +59,20 @@ pub(crate) fn r#enum_def(s: &ItemEnum) -> syn::Result { Ok(DerivedTS { crate_rename, inline: quote!([#(#formatted_variants),*].join(#separator)), - inline_flattened: enum_attr.repr.is_none().then_some({ - if formatted_none.is_empty() { - quote!(format!("({})", [#(#formatted_variants),*].join(" | "))) - } else { + inline_flattened: enum_attr.repr.is_none().then_some(quote!( + format!("({})", [#(#formatted_variants),*].join(" | ")) + )), + optional_inline_flattened: if !formatted_optional_variants.is_empty() { + enum_attr.repr.is_none().then_some( quote!(format!( - "({} | {})", - [#(#formatted_variants),*].join(" | "), - [#(#formatted_none),*].join(" | ") + "({} | {{ {} }})", + [#(#formatted_optional_variants),*].join(" | "), + [#(#formatted_never),*].join("; "), )) - } - }), + ) + } else { + None + }, dependencies, docs: enum_attr.docs, export: enum_attr.export, @@ -83,6 +88,7 @@ pub(crate) fn r#enum_def(s: &ItemEnum) -> syn::Result { fn format_variant( formatted_variants: &mut Vec, formatted_optional_variants: &mut Vec, + formatted_never: &mut Vec, dependencies: &mut Dependencies, enum_attr: &EnumAttr, variant: &Variant, @@ -167,11 +173,16 @@ fn format_variant( } else { ( quote!(format!("{{ \"{}\": {} }}", #ts_name, #parsed_ty)), - Some(quote!(format!("{{ \"{}\": {}; {} }}", #ts_name, #parsed_ty, #never_ty))) + Some(quote!( + format!("{{ \"{}\": {}; {} }}", #ts_name, #parsed_ty, #never_ty) + )), ) } } - _ => (quote!(format!("{{ \"{}\": {} }}", #ts_name, #parsed_ty)), None), + _ => ( + quote!(format!("{{ \"{}\": {} }}", #ts_name, #parsed_ty)), + None, + ), }, (false, Tagged::Adjacently { tag, content }) => match &variant.fields { Fields::Unnamed(unnamed) if unnamed.unnamed.len() == 1 => { @@ -183,24 +194,37 @@ fn format_variant( let field_ty = field_attr.type_as(&field.ty); if field_attr.skip { - (quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #ts_name)), None) + ( + quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #ts_name)), + None, + ) } else { let ty = match field_attr.type_override { Some(type_override) => quote!(#type_override), None => quote!(<#field_ty as #crate_rename::TS>::name(cfg)) }; - (quote!(format!("{{ \"{}\": \"{}\", \"{}\": {} }}", #tag, #ts_name, #content, #ty)), None) + ( + quote!( + format!("{{ \"{}\": \"{}\", \"{}\": {} }}", #tag, #ts_name, #content, #ty) + ), + None, + ) } } - Fields::Unit => (quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #ts_name)), None), - _ => (quote!(format!("{{ \"{}\": \"{}\", \"{}\": {} }}", #tag, #ts_name, #content, #parsed_ty)), None) - + Fields::Unit => ( + quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #ts_name)), + None, + ), + _ => ( + quote!( + format!("{{ \"{}\": \"{}\", \"{}\": {} }}", #tag, #ts_name, #content, #parsed_ty) + ), + None, + ), }, (false, Tagged::Internally { tag }) => match variant_type.inline_flattened { - Some(_) => { - (quote! { #parsed_ty }, None) - } + Some(_) => (quote! { #parsed_ty }, None), None => match &variant.fields { Fields::Unnamed(unnamed) if unnamed.unnamed.len() == 1 => { let field = &unnamed.unnamed[0]; @@ -211,7 +235,10 @@ fn format_variant( let field_ty = field_attr.type_as(&field.ty); if field_attr.skip { - (quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #ts_name)), None) + ( + quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #ts_name)), + None, + ) } else { let ty = match field_attr.type_override { Some(type_override) => quote! { #type_override }, @@ -220,11 +247,20 @@ fn format_variant( } }; - (quote!(format!("{{ \"{}\": \"{}\" }} & {}", #tag, #ts_name, #ty)), None) + ( + quote!(format!("{{ \"{}\": \"{}\" }} & {}", #tag, #ts_name, #ty)), + None, + ) } } - Fields::Unit => (quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #ts_name)), None), - _ => (quote!(format!("{{ \"{}\": \"{}\" }} & {}", #tag, #ts_name, #parsed_ty)), None) + Fields::Unit => ( + quote!(format!("{{ \"{}\": \"{}\" }}", #tag, #ts_name)), + None, + ), + _ => ( + quote!(format!("{{ \"{}\": \"{}\" }} & {}", #tag, #ts_name, #parsed_ty)), + None, + ), }, }, }; @@ -232,6 +268,7 @@ fn format_variant( formatted_variants.push(formatted); if let Some(formatted) = formatted_optional { formatted_optional_variants.push(formatted); + formatted_never.push(never_ty); } Ok(()) } @@ -244,6 +281,7 @@ fn empty_enum(ts_name: Expr, enum_attr: EnumAttr) -> DerivedTS { inline: quote!("never".to_owned()), docs: enum_attr.docs, inline_flattened: None, + optional_inline_flattened: None, dependencies: Dependencies::new(crate_rename), export: enum_attr.export, export_to: enum_attr.export_to, diff --git a/macros/src/types/newtype.rs b/macros/src/types/newtype.rs index b4edb675..c584c232 100644 --- a/macros/src/types/newtype.rs +++ b/macros/src/types/newtype.rs @@ -43,6 +43,7 @@ pub(crate) fn newtype( crate_rename: crate_rename.clone(), inline: inline_def, inline_flattened: None, + optional_inline_flattened: None, docs: attr.docs.clone(), dependencies, export: attr.export, diff --git a/macros/src/types/tuple.rs b/macros/src/types/tuple.rs index d440b747..11b9cd55 100644 --- a/macros/src/types/tuple.rs +++ b/macros/src/types/tuple.rs @@ -32,6 +32,7 @@ pub(crate) fn tuple(attr: &StructAttr, ts_name: Expr, fields: &FieldsUnnamed) -> ) }, inline_flattened: None, + optional_inline_flattened: None, docs: attr.docs.clone(), dependencies, export: attr.export, diff --git a/macros/src/types/type_as.rs b/macros/src/types/type_as.rs index 27f98b0e..f0bd38b0 100644 --- a/macros/src/types/type_as.rs +++ b/macros/src/types/type_as.rs @@ -21,6 +21,7 @@ pub(crate) fn type_as_struct( crate_rename: crate_rename.clone(), inline: quote!(<#type_as as #crate_rename::TS>::inline(cfg)), inline_flattened: None, + optional_inline_flattened: None, docs: attr.docs.clone(), dependencies, export: attr.export, @@ -43,6 +44,7 @@ pub(crate) fn type_as_enum(attr: &EnumAttr, ts_name: Expr, type_as: &Type) -> Re crate_rename: crate_rename.clone(), inline: quote!(<#type_as as #crate_rename::TS>::inline(cfg)), inline_flattened: None, + optional_inline_flattened: None, docs: attr.docs.clone(), dependencies, export: attr.export, @@ -51,10 +53,6 @@ pub(crate) fn type_as_enum(attr: &EnumAttr, ts_name: Expr, type_as: &Type) -> Re concrete: attr.concrete.clone(), bound: attr.bound.clone(), ts_enum: None, -<<<<<<< HEAD is_enum: quote!(<#type_as as #crate_rename::TS>::IS_ENUM), -======= - is_enum: false, ->>>>>>> a316b00 (feat: parse optional flat enums) }) } diff --git a/macros/src/types/type_override.rs b/macros/src/types/type_override.rs index 653ed1df..3042cfa7 100644 --- a/macros/src/types/type_override.rs +++ b/macros/src/types/type_override.rs @@ -18,6 +18,7 @@ pub(crate) fn type_override_struct( crate_rename: crate_rename.clone(), inline: quote!(#type_override.to_owned()), inline_flattened: None, + optional_inline_flattened: None, docs: attr.docs.clone(), dependencies: Dependencies::new(crate_rename), export: attr.export, @@ -41,6 +42,7 @@ pub(crate) fn type_override_enum( crate_rename: crate_rename.clone(), inline: quote!(#type_override.to_owned()), inline_flattened: None, + optional_inline_flattened: None, docs: attr.docs.clone(), dependencies: Dependencies::new(crate_rename), export: attr.export, @@ -49,10 +51,6 @@ pub(crate) fn type_override_enum( concrete: attr.concrete.clone(), bound: attr.bound.clone(), ts_enum: None, -<<<<<<< HEAD is_enum: quote!(true), // we dont know what the override is, so we preserve is_enum -======= - is_enum: false, ->>>>>>> a316b00 (feat: parse optional flat enums) }) } diff --git a/macros/src/types/unit.rs b/macros/src/types/unit.rs index e2a6cb29..001273f1 100644 --- a/macros/src/types/unit.rs +++ b/macros/src/types/unit.rs @@ -14,6 +14,7 @@ pub(crate) fn empty_object(attr: &StructAttr, ts_name: Expr) -> DerivedTS { crate_rename: crate_rename.clone(), inline: quote!("Record".to_owned()), inline_flattened: None, + optional_inline_flattened: None, docs: attr.docs.clone(), dependencies: Dependencies::new(crate_rename), export: attr.export, @@ -33,6 +34,7 @@ pub(crate) fn empty_array(attr: &StructAttr, ts_name: Expr) -> DerivedTS { crate_rename: crate_rename.clone(), inline: quote!("never[]".to_owned()), inline_flattened: None, + optional_inline_flattened: None, docs: attr.docs.clone(), dependencies: Dependencies::new(crate_rename), export: attr.export, @@ -41,11 +43,7 @@ pub(crate) fn empty_array(attr: &StructAttr, ts_name: Expr) -> DerivedTS { concrete: attr.concrete.clone(), bound: attr.bound.clone(), ts_enum: None, -<<<<<<< HEAD is_enum: quote!(false), -======= - is_enum: false, ->>>>>>> a316b00 (feat: parse optional flat enums) } } @@ -56,6 +54,7 @@ pub(crate) fn null(attr: &StructAttr, ts_name: Expr) -> DerivedTS { crate_rename: crate_rename.clone(), inline: quote!("null".to_owned()), inline_flattened: None, + optional_inline_flattened: None, docs: attr.docs.clone(), dependencies: Dependencies::new(crate_rename), export: attr.export, @@ -64,10 +63,6 @@ pub(crate) fn null(attr: &StructAttr, ts_name: Expr) -> DerivedTS { concrete: attr.concrete.clone(), bound: attr.bound.clone(), ts_enum: None, -<<<<<<< HEAD is_enum: quote!(false), -======= - is_enum: false, ->>>>>>> a316b00 (feat: parse optional flat enums) } } diff --git a/ts-rs/src/lib.rs b/ts-rs/src/lib.rs index 4e8539e9..3cb59c89 100644 --- a/ts-rs/src/lib.rs +++ b/ts-rs/src/lib.rs @@ -379,6 +379,7 @@ pub trait TS { /// // ... /// # fn name(_: &ts_rs::Config) -> String { todo!() } /// # fn inline(_: &ts_rs::Config) -> String { todo!() } + /// # fn optional_inline_flattened(_: &ts_rs::Config) -> String { todo!() } /// } /// ``` type WithoutGenerics: TS + ?Sized; @@ -443,6 +444,10 @@ pub trait TS { panic!("{} cannot be flattened", Self::name(cfg)) } + /// Flatten an optional type declaration. + /// This function will panic if the type cannot be flattened. + fn optional_inline_flattened() -> String; + /// Iterates over all dependency of this type. fn visit_dependencies(_: &mut impl TypeVisitor) where @@ -732,6 +737,7 @@ macro_rules! impl_primitives { type OptionInnerType = Self; fn name(_: &$crate::Config) -> String { String::from($l) } fn inline(cfg: &$crate::Config) -> String { ::name(cfg) } + fn optional_inline_flattened(cfg: &$crate::Config) -> String { ::name(cfg) } } )*)* }; } @@ -772,6 +778,7 @@ macro_rules! impl_tuples { fn inline_flattened(_: &$crate::Config) -> String { panic!("tuple cannot be flattened") } fn decl(_: &$crate::Config) -> String { panic!("tuple cannot be declared") } fn decl_concrete(_: &$crate::Config) -> String { panic!("tuple cannot be declared") } + fn optional_inline_flattened(_: &$crate::Config) -> String { panic!("tuple cannot be declared") } } }; ( $i2:ident $(, $i:ident)* ) => { @@ -790,6 +797,7 @@ macro_rules! impl_wrapper { fn name(cfg: &$crate::Config) -> String { ::name(cfg) } fn inline(cfg: &$crate::Config) -> String { ::inline(cfg) } fn inline_flattened(cfg: &$crate::Config) -> String { ::inline_flattened(cfg) } + fn optional_inline_flattened(cfg: &$crate::Config) -> String { ::optional_inline_flattened(cfg) } fn visit_dependencies(v: &mut impl TypeVisitor) where Self: 'static, @@ -820,6 +828,7 @@ macro_rules! impl_shadow { fn name(cfg: &$crate::Config) -> String { <$s as $crate::TS>::name(cfg) } fn inline(cfg: &$crate::Config) -> String { <$s as $crate::TS>::inline(cfg) } fn inline_flattened(cfg: &$crate::Config) -> String { <$s as $crate::TS>::inline_flattened(cfg) } + fn optional_inline_flattened(cfg: &$crate::Config) -> String { <$s as $crate::TS>::optional_inline_flattened(cfg) } fn visit_dependencies(v: &mut impl $crate::TypeVisitor) where Self: 'static, @@ -1044,6 +1053,10 @@ impl TS for HashMap { fn inline_flattened(cfg: &Config) -> String { format!("({})", Self::inline(cfg)) } + + fn optional_inline_flattened() -> String { + panic!("{} cannot be flattened", ::name()) + } } // TODO: replace manual impl with dummy struct & `impl_shadow` (like for `JsonValue`) @@ -1074,6 +1087,10 @@ impl TS for Range { fn inline(cfg: &Config) -> String { panic!("{} cannot be inlined", Self::name(cfg)) } + + fn optional_inline_flattened() -> String { + panic!("{} cannot be flattened", ::name()) + } } impl_shadow!(as Range: impl TS for RangeInclusive); @@ -1193,6 +1210,10 @@ impl TS for Dummy { fn inline(cfg: &Config) -> String { panic!("{} cannot be inlined", Self::name(cfg)) } + + fn optional_inline_flattened() -> String { + panic!("{} cannot be flattened", ::name()) + } } /// Formats rust doc comments, turning them into a JSDoc comments. diff --git a/ts-rs/tests/integration/issue_308.rs b/ts-rs/tests/integration/issue_308.rs index 524d028d..5a8de577 100644 --- a/ts-rs/tests/integration/issue_308.rs +++ b/ts-rs/tests/integration/issue_308.rs @@ -13,6 +13,7 @@ trait Malicious { fn name() -> String { unimplemented!() } fn inline() -> String { unimplemented!() } fn inline_flattened() -> String { unimplemented!() } + fn optional_inline_flattened() -> String { unimplemented!() } fn dependencies() -> Vec { unimplemented!() } fn visit_dependencies(_: &mut impl TypeVisitor) { unimplemented!() } fn visit_generics(_: &mut impl TypeVisitor) { unimplemented!() } diff --git a/ts-rs/tests/integration/optional_flattened_enum.rs b/ts-rs/tests/integration/optional_flattened_enum.rs index 7051fa3f..8645a600 100644 --- a/ts-rs/tests/integration/optional_flattened_enum.rs +++ b/ts-rs/tests/integration/optional_flattened_enum.rs @@ -18,7 +18,7 @@ fn optional_flatten_enum_adds_empty_object() { } assert_eq!( - T::inline_flattened(), + T::optional_inline_flattened(), r#"{ a: string, } & ({ "firstOption": string; "secondOption"?: never } | { "secondOption": boolean; "firstOption"?: never } | { "firstOption"?: never; "secondOption"?: never })"# ); } @@ -41,7 +41,7 @@ fn optional_flatten_unit_variants_adds_empty_object() { } assert_eq!( - T::inline_flattened(), + T::optional_inline_flattened(), r#"{ a: string, } & ("first" | "second" | "third" | { "first"?: never; "second"?: never; "third"?: never })"# ); } @@ -64,7 +64,7 @@ fn optional_flatten_mixed_variants_adds_empty_object() { } assert_eq!( - T::inline_flattened(), + T::optional_inline_flattened(), r#"{ a: string, } & ("unit" | { "tuple": [number, string]; "struct"?: never; "unit"?: never } | { "struct": { x: number, y: string, }; "tuple"?: never; "unit"?: never } | { "unit"?: never; "tuple"?: never; "struct"?: never })"# ); } @@ -91,7 +91,7 @@ fn optional_flatten_with_nested_objects_adds_empty_object() { } assert_eq!( - T::inline_flattened(), + T::optional_inline_flattened(), r#"{ a: string, } & ({ "first": Inner; "second"?: never } | { "second": Inner; "first"?: never } | { "first"?: never; "second"?: never })"# ); } @@ -119,7 +119,7 @@ fn multiple_optional_flattened_enums_each_add_empty_object() { b_enum: Option, } - let result = T::inline_flattened(); + let result = T::optional_inline_flattened(); assert!(result.contains(r#"{ "optionA": string; "optionA"?: never } | { "optionA"?: never }"#)); assert!(result.contains(r#"{ "optionB": number; "optionB"?: never } | { "optionB"?: never }"#)); @@ -141,7 +141,7 @@ fn optional_flatten_with_rename_all_kebab_case() { flattened: Option, } - let result = T::inline_flattened(); + let result = T::optional_inline_flattened(); assert!(result.contains(r#""first-option": string"#)); assert!(result.contains(r#""second-option": boolean"#)); @@ -165,7 +165,7 @@ fn optional_flatten_single_variant_adds_empty_object() { } assert_eq!( - T::inline_flattened(), + T::optional_inline_flattened(), r#"{ a: string, } & ({ "only": string } | { "only"?: never })"# ); } @@ -187,7 +187,7 @@ fn non_optional_flatten_enum_is_unchanged() { } assert_eq!( - T::inline_flattened(), + T::optional_inline_flattened(), r#"{ a: string, } & ({ "firstOption": string } | { "secondOption": boolean })"# ); } From 5aa91ce990c8eaf317468b361815fb509ef188a4 Mon Sep 17 00:00:00 2001 From: 4dawit Date: Fri, 9 Jan 2026 17:05:07 -0600 Subject: [PATCH 3/6] wip: added other options, need to retroactively add skipped past options --- macros/src/types/enum.rs | 30 ++++++++++++------- macros/src/types/unit.rs | 4 +++ ts-rs/src/lib.rs | 6 ++-- .../integration/optional_flattened_enum.rs | 2 +- 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/macros/src/types/enum.rs b/macros/src/types/enum.rs index c90759fd..63ca62ed 100644 --- a/macros/src/types/enum.rs +++ b/macros/src/types/enum.rs @@ -63,13 +63,11 @@ pub(crate) fn r#enum_def(s: &ItemEnum) -> syn::Result { format!("({})", [#(#formatted_variants),*].join(" | ")) )), optional_inline_flattened: if !formatted_optional_variants.is_empty() { - enum_attr.repr.is_none().then_some( - quote!(format!( - "({} | {{ {} }})", - [#(#formatted_optional_variants),*].join(" | "), - [#(#formatted_never),*].join("; "), - )) - ) + enum_attr.repr.is_none().then_some(quote!(format!( + "({} | {{ {} }})", + [#(#formatted_optional_variants),*].join(" | "), + [#(#formatted_never),*].join("; "), + ))) } else { None }, @@ -164,17 +162,27 @@ fn format_variant( Fields::Unit => (quote!(format!("\"{}\"", #ts_name)), None), Fields::Unnamed(unnamed) if unnamed.unnamed.len() == 1 => { let field = &unnamed.unnamed[0]; - let field_attr = FieldAttr::from_attrs(&unnamed.unnamed[0].attrs)?; + let field_attr = FieldAttr::from_attrs(&field.attrs)?; field_attr.assert_validity(field)?; if field_attr.skip { (quote!(format!("\"{}\"", #ts_name)), None) } else { + let other_options = if !formatted_never.is_empty() { + // if let Some(past_option) = formatted_optional_variants.get_mut(index - 1) { + // quote!(#past_option.replace("}", format!("{}; }", #never_ty))); + // } + + quote!([#(#formatted_never),*].join("; ")) + } else { + quote!("") + }; + ( quote!(format!("{{ \"{}\": {} }}", #ts_name, #parsed_ty)), Some(quote!( - format!("{{ \"{}\": {}; {} }}", #ts_name, #parsed_ty, #never_ty) + format!("{{ \"{}\": {}; {}}}", #ts_name, #parsed_ty, #other_options) )), ) } @@ -187,7 +195,7 @@ fn format_variant( (false, Tagged::Adjacently { tag, content }) => match &variant.fields { Fields::Unnamed(unnamed) if unnamed.unnamed.len() == 1 => { let field = &unnamed.unnamed[0]; - let field_attr = FieldAttr::from_attrs(&unnamed.unnamed[0].attrs)?; + let field_attr = FieldAttr::from_attrs(&field.attrs)?; field_attr.assert_validity(field)?; @@ -228,7 +236,7 @@ fn format_variant( None => match &variant.fields { Fields::Unnamed(unnamed) if unnamed.unnamed.len() == 1 => { let field = &unnamed.unnamed[0]; - let field_attr = FieldAttr::from_attrs(&unnamed.unnamed[0].attrs)?; + let field_attr = FieldAttr::from_attrs(&field.attrs)?; field_attr.assert_validity(field)?; diff --git a/macros/src/types/unit.rs b/macros/src/types/unit.rs index 001273f1..b8166135 100644 --- a/macros/src/types/unit.rs +++ b/macros/src/types/unit.rs @@ -23,7 +23,11 @@ pub(crate) fn empty_object(attr: &StructAttr, ts_name: Expr) -> DerivedTS { concrete: attr.concrete.clone(), bound: attr.bound.clone(), ts_enum: None, +<<<<<<< HEAD is_enum: quote!(false), +======= + is_enum: false, +>>>>>>> d212ed6 (wip: added other options, need to retroactively add skipped past options) } } diff --git a/ts-rs/src/lib.rs b/ts-rs/src/lib.rs index 3cb59c89..4337fddc 100644 --- a/ts-rs/src/lib.rs +++ b/ts-rs/src/lib.rs @@ -446,7 +446,7 @@ pub trait TS { /// Flatten an optional type declaration. /// This function will panic if the type cannot be flattened. - fn optional_inline_flattened() -> String; + fn optional_inline_flattened(cfg: &Config) -> String; /// Iterates over all dependency of this type. fn visit_dependencies(_: &mut impl TypeVisitor) @@ -863,9 +863,9 @@ impl TS for Option { fn inline_flattened(cfg: &Config) -> String { if ::IS_ENUM { - ::inline_flattened(cfg) + ::optional_inline_flattened(cfg) } else { - panic!("{} cannot be flattened", ::name(cfg)) + ::inline_flattened(cfg) } } diff --git a/ts-rs/tests/integration/optional_flattened_enum.rs b/ts-rs/tests/integration/optional_flattened_enum.rs index 8645a600..b1a2f7d7 100644 --- a/ts-rs/tests/integration/optional_flattened_enum.rs +++ b/ts-rs/tests/integration/optional_flattened_enum.rs @@ -166,7 +166,7 @@ fn optional_flatten_single_variant_adds_empty_object() { assert_eq!( T::optional_inline_flattened(), - r#"{ a: string, } & ({ "only": string } | { "only"?: never })"# + r#"{ a: string, } & ({ "only": string; } | { "only"?: never })"# ); } From b4fe93a5afca0ec6a4f02b9e0d394a5e851d8ce2 Mon Sep 17 00:00:00 2001 From: 4dawit Date: Mon, 12 Jan 2026 20:03:00 -0600 Subject: [PATCH 4/6] feat: finish never field option logic --- Cargo.lock | 865 ++++++++++-------- macros/src/types/enum.rs | 98 +- .../integration/optional_flattened_enum.rs | 72 +- 3 files changed, 562 insertions(+), 473 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b96d94f5..0e751aac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,29 +2,14 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "addr2line" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" - [[package]] name = "ahash" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", - "getrandom", + "getrandom 0.3.4", "once_cell", "version_check", "zerocopy", @@ -36,12 +21,6 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -53,53 +32,41 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.93" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] -name = "arrayvec" -version = "0.7.6" +name = "ar_archive_writer" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +checksum = "f0c269894b6fe5e9d7ada0cf69b5bf847ff35bc25fc271f08e1d080fce80339a" +dependencies = [ + "object", +] [[package]] name = "ast_node" -version = "3.0.4" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a184645bcc6f52d69d8e7639720699c6a99efb711f886e251ed1d16db8dd90e" +checksum = "2eb025ef00a6da925cf40870b9c8d008526b6004ece399cb0974209720f0b194" dependencies = [ "quote", "swc_macros_common", - "syn 2.0.90", + "syn 2.0.114", ] [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" - -[[package]] -name = "backtrace" -version = "0.3.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets", -] +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "base64" -version = "0.13.1" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "better_scoped_tls" @@ -112,9 +79,9 @@ dependencies = [ [[package]] name = "bigdecimal" -version = "0.4.6" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f850665a0385e070b64c38d2354e6c104c8479c59868d1e48a0c13ee2c7a1c1" +checksum = "4d6867f1565b3aad85681f1015055b087fcfd840d6aeee6eee7f2da317603695" dependencies = [ "autocfg", "libm", @@ -126,9 +93,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.6.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "bitvec" @@ -144,27 +111,29 @@ dependencies = [ [[package]] name = "borsh" -version = "1.5.3" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2506947f73ad44e344215ccd6403ac2ae18cd8e046e581a441bf8d199f257f03" +checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" dependencies = [ "cfg_aliases", ] [[package]] name = "bson" -version = "2.13.0" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "068208f2b6fcfa27a7f1ee37488d2bb8ba2640f68f5475d08e1d9130696aba59" +checksum = "7969a9ba84b0ff843813e7249eed1678d9b6607ce5a3b8f0a47af3fcf7978e6e" dependencies = [ "ahash", "base64", "bitvec", + "getrandom 0.2.17", + "getrandom 0.3.4", "hex", "indexmap", "js-sys", "once_cell", - "rand", + "rand 0.9.2", "serde", "serde_bytes", "serde_json", @@ -174,9 +143,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" dependencies = [ "allocator-api2", ] @@ -189,9 +158,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "bytes-str" @@ -220,23 +189,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b4a6cae9efc04cc6cbb8faf338d2c497c165c83e74509cf4dbedea948bbf6e5" dependencies = [ "quote", - "syn 2.0.90", + "syn 2.0.114", ] [[package]] name = "cc" -version = "1.2.2" +version = "1.2.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f34d93e62b03caf570cccc334cbc6c2fceca82f39211051345108adcba3eebdc" +checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3" dependencies = [ + "find-msvc-tools", "shlex", ] [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" @@ -246,17 +216,16 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ - "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "serde", "wasm-bindgen", - "windows-targets", + "windows-link", ] [[package]] @@ -267,15 +236,15 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "data-url" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" +checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" [[package]] name = "deno_ast" -version = "0.50.2" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2e510f96dd268de7ef55264ca3bafe456b95ea63a67b7d9a186b4196eb2e484" +checksum = "30c2f6f65154faed61e45d6578566f9fab9d2a330c35c87366706883701cce51" dependencies = [ "capacity_builder", "deno_error", @@ -292,15 +261,15 @@ dependencies = [ "swc_eq_ignore_macros", "text_lines", "thiserror", - "unicode-width 0.2.1", + "unicode-width", "url", ] [[package]] name = "deno_error" -version = "0.7.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde60bd153886964234c5012d3d9caf788287f28d81fb24a884436904101ef10" +checksum = "3007d3f1ea92ea503324ae15883aac0c2de2b8cf6fead62203ff6a67161007ab" dependencies = [ "deno_error_macro", "libc", @@ -308,20 +277,20 @@ dependencies = [ [[package]] name = "deno_error_macro" -version = "0.7.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "409f265785bd946d3006756955aaf40b0e4deb25752eae6a990afe54a31cfd83" +checksum = "9b565e60a9685cdf312c888665b5f8647ac692a7da7e058a5e2268a466da8eaf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.114", ] [[package]] name = "deno_media_type" -version = "0.2.9" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0ec0dada9dc5ac4733b4175d36f6a150b7dd68fab46db35cb1ef00dd7366acb" +checksum = "9fd0af4161f90b092feb363864a64d7c74e0efc13a15905d0d09df73bb72a123" dependencies = [ "data-url", "serde", @@ -330,9 +299,9 @@ dependencies = [ [[package]] name = "deno_terminal" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23f71c27009e0141dedd315f1dfa3ebb0a6ca4acce7c080fac576ea415a465f6" +checksum = "f3ba8041ae7319b3ca6a64c399df4112badcbbe0868b4517637647614bede4be" dependencies = [ "once_cell", "termcolor", @@ -340,9 +309,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.11" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", ] @@ -355,7 +324,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.114", ] [[package]] @@ -366,11 +335,11 @@ checksum = "2c1d827947704a9495f705d6aeed270fa21a67f825f22902c28f38dc3af7a9ae" dependencies = [ "anyhow", "bumpalo", - "hashbrown 0.15.2", + "hashbrown 0.15.5", "indexmap", "rustc-hash", "serde", - "unicode-width 0.2.1", + "unicode-width", ] [[package]] @@ -385,9 +354,9 @@ dependencies = [ [[package]] name = "dprint-plugin-typescript" -version = "0.95.11" +version = "0.95.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74aa5df06eb156f7df91bf17a0ca65fd349dfa22463c3eab0a019bab20df12df" +checksum = "f8c3e39abf274d7f85a017691da2d4c78db0141425d05201b7249de4a48cabd1" dependencies = [ "anyhow", "capacity_builder", @@ -401,9 +370,9 @@ dependencies = [ [[package]] name = "dprint-swc-ext" -version = "0.25.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48928f46665a21bca006dc9a02d1329143ef161f7347f2b9430b55519275db8a" +checksum = "33175ddb7a6d418589cab2966bd14a710b3b1139459d3d5ca9edf783c4833f4c" dependencies = [ "allocator-api2", "bumpalo", @@ -419,15 +388,15 @@ dependencies = [ [[package]] name = "either" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "example" @@ -439,6 +408,12 @@ dependencies = [ "uuid", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41" + [[package]] name = "foldhash" version = "0.1.5" @@ -447,21 +422,21 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] [[package]] name = "from_variant" -version = "2.0.2" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308530a56b099da144ebc5d8e179f343ad928fa2b3558d1eb3db9af18d6eff43" +checksum = "e5ff35a391aef949120a0340d690269b3d9f63460a6106e99bd07b961f345ea9" dependencies = [ "swc_macros_common", - "syn 2.0.90", + "syn 2.0.114", ] [[package]] @@ -472,20 +447,30 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] -name = "gimli" -version = "0.31.1" +name = "getrandom" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasip2", + "wasm-bindgen", +] [[package]] name = "hash32" @@ -508,15 +493,21 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", "foldhash", ] +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + [[package]] name = "heapless" version = "0.8.0" @@ -541,27 +532,29 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hstr" -version = "2.0.1" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced1416104790052518d199e753d49a7d8130d476c664bc9e53f40cfecb8e615" +checksum = "0c43c0a9e8fbdb3bb9dc8eee85e1e2ac81605418b4c83b6b7413cbf14d56ca5c" dependencies = [ "hashbrown 0.14.5", "new_debug_unreachable", "once_cell", "rustc-hash", + "serde", "triomphe", ] [[package]] name = "iana-time-zone" -version = "0.1.61" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", "windows-core", ] @@ -577,21 +570,22 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", + "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", @@ -600,104 +594,66 @@ dependencies = [ "zerovec", ] -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ - "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.0" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", + "icu_locale_core", "writeable", "yoke", "zerofrom", + "zerotrie", "zerovec", ] -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", -] - [[package]] name = "idna" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", @@ -706,9 +662,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -716,13 +672,14 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.9.0" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.16.1", "serde", + "serde_core", ] [[package]] @@ -734,27 +691,39 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.114", ] [[package]] name = "itoa" -version = "1.0.14" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "jiff" -version = "0.2.1" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3590fea8e9e22d449600c9bbd481a8163bef223e4ff938e5f55899f8cf1adb93" +checksum = "e67e8da4c49d6d9909fe03361f9b620f58898859f5c7aded68351e85e71ecf50" dependencies = [ + "jiff-static", "jiff-tzdb-platform", "log", "portable-atomic", "portable-atomic-util", - "serde", - "windows-sys", + "serde_core", + "windows-sys 0.61.2", +] + +[[package]] +name = "jiff-static" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0c84ee7f197eca9a86c6fd6cb771e55eb991632f15f2bc3ca6ec838929e6e78" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", ] [[package]] @@ -774,9 +743,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.74" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a865e038f7f6ed956f788f0d7d60c541fff74c7bd74272c5d4cf15c63743e705" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", @@ -784,42 +753,33 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.167" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "libm" -version = "0.2.11" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "litemap" -version = "0.7.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "log" -version = "0.4.22" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[package]] -name = "miniz_oxide" -version = "0.8.0" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" -dependencies = [ - "adler2", -] +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "new_debug_unreachable" @@ -864,9 +824,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.5" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] @@ -879,24 +839,24 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "ordered-float" -version = "4.5.0" +version = "5.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c65ee1f9701bf938026630b455d5315f490640234259037edb259798b3bcf85e" +checksum = "7f4779c6901a562440c3786d08192c6fbda7c1c2060edd10006b05ee35d10f2d" dependencies = [ "num-traits", ] [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "phf" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ "phf_macros", "phf_shared", @@ -904,47 +864,47 @@ dependencies = [ [[package]] name = "phf_generator" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared", - "rand", + "rand 0.8.5", ] [[package]] name = "phf_macros" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ "phf_generator", "phf_shared", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.114", ] [[package]] name = "phf_shared" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ - "siphasher", + "siphasher 1.0.1", ] [[package]] name = "pin-project-lite" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "portable-atomic" -version = "1.11.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" [[package]] name = "portable-atomic-util" @@ -955,6 +915,15 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -963,40 +932,47 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ "zerocopy", ] [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" dependencies = [ "unicode-ident", ] [[package]] name = "psm" -version = "0.1.24" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" +checksum = "d11f2fedc3b7dafdc2851bc52f277377c5473d378859be234bc7ebb593144d01" dependencies = [ + "ar_archive_writer", "cc", ] [[package]] name = "quote" -version = "1.0.37" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "radium" version = "0.7.0" @@ -1009,19 +985,27 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ - "libc", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ "rand_chacha", - "rand_core", + "rand_core 0.9.4", ] [[package]] name = "rand_chacha" -version = "0.3.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.9.4", ] [[package]] @@ -1029,15 +1013,15 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] [[package]] -name = "rustc-demangle" -version = "0.1.24" +name = "rand_core" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "4f1b3bc831f92381018fd9c6350b917c7b21f1eed35a65a51900e0e55a3d7afa" +dependencies = [ + "getrandom 0.3.4", +] [[package]] name = "rustc-hash" @@ -1046,10 +1030,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] -name = "ryu" -version = "1.0.18" +name = "rustversion" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "scoped-tls" @@ -1059,9 +1043,9 @@ checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" [[package]] name = "semver" -version = "1.0.23" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" [[package]] name = "seq-macro" @@ -1071,9 +1055,9 @@ checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc" [[package]] name = "serde" -version = "1.0.225" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6c24dee235d0da097043389623fb913daddf92c76e9f5a1db88607a0bcbd1d" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", @@ -1081,44 +1065,46 @@ dependencies = [ [[package]] name = "serde_bytes" -version = "0.11.15" +version = "0.11.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" dependencies = [ "serde", + "serde_core", ] [[package]] name = "serde_core" -version = "1.0.225" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "659356f9a0cb1e529b24c01e43ad2bdf520ec4ceaf83047b83ddcc2251f96383" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.225" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea936adf78b1f766949a4977b91d2f5595825bd6ec079aa9543ad2685fc4516" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.114", ] [[package]] name = "serde_json" -version = "1.0.133" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "indexmap", "itoa", "memchr", - "ryu", "serde", + "serde_core", + "zmij", ] [[package]] @@ -1133,11 +1119,17 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "smallvec" -version = "1.13.2" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "smartstring" @@ -1152,31 +1144,31 @@ dependencies = [ [[package]] name = "smol_str" -version = "0.3.2" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9676b89cd56310a87b93dec47b11af744f34d5fc9f367b829474eec0a891350d" +checksum = "0f7a918bd2a9951d18ee6e48f076843e8e73a9a5d22cf05bcd4b7a81bdd04e17" dependencies = [ "borsh", - "serde", + "serde_core", ] [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "stacker" -version = "0.1.17" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b" +checksum = "e1f8b29fb42aafcea4edeeb6b2f2d7ecd0d969c48b4cf0d2e64aafc471dd6e59" dependencies = [ "cc", "cfg-if", "libc", "psm", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -1193,14 +1185,14 @@ checksum = "ae36a4951ca7bd1cfd991c241584a9824a70f6aff1e7d4f693fb3f2465e4030e" dependencies = [ "quote", "swc_macros_common", - "syn 2.0.90", + "syn 2.0.114", ] [[package]] name = "swc_atoms" -version = "7.0.0" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3500dcf04c84606b38464561edc5e46f5132201cb3e23cf9613ed4033d6b1bb2" +checksum = "d4ccbe2ecad10ad7432100f878a107b1d972a8aee83ca53184d00c23a078bb8a" dependencies = [ "hstr", "once_cell", @@ -1209,9 +1201,9 @@ dependencies = [ [[package]] name = "swc_common" -version = "14.0.4" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2bb772b3a26b8b71d4e8c112ced5b5867be2266364b58517407a270328a2696" +checksum = "259b675d633a26d24efe3802a9d88858c918e6e8f062d3222d3aa02d56a2cf4c" dependencies = [ "anyhow", "ast_node", @@ -1224,20 +1216,20 @@ dependencies = [ "once_cell", "rustc-hash", "serde", - "siphasher", + "siphasher 0.3.11", "swc_atoms", "swc_eq_ignore_macros", "swc_visit", "tracing", - "unicode-width 0.1.14", + "unicode-width", "url", ] [[package]] name = "swc_ecma_ast" -version = "15.0.0" +version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65c25af97d53cf8aab66a6c68f3418663313fc969ad267fc2a4d19402c329be1" +checksum = "a573a0c72850dec8d4d8085f152d5778af35a2520c3093b242d2d1d50776da7c" dependencies = [ "bitflags", "is-macro", @@ -1255,15 +1247,13 @@ dependencies = [ [[package]] name = "swc_ecma_lexer" -version = "23.0.1" +version = "26.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c3bd958a5a67e2cc3f74abdd41fda688e54e7a25b866569260ef7018b67972" +checksum = "5e82f7747e052c6ff6e111fa4adeb14e33b46ee6e94fe5ef717601f651db48fc" dependencies = [ - "arrayvec", "bitflags", "either", "num-bigint", - "phf", "rustc-hash", "seq-macro", "serde", @@ -1273,22 +1263,28 @@ dependencies = [ "swc_atoms", "swc_common", "swc_ecma_ast", + "swc_ecma_parser", "tracing", ] [[package]] name = "swc_ecma_parser" -version = "23.0.0" +version = "27.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9166873bb660bed50b5f422233537d3e946336398570a4a13e57d8c63d6a01c5" +checksum = "7f1a51af1a92cd4904c073b293e491bbc0918400a45d58227b34c961dd6f52d7" dependencies = [ + "bitflags", "either", "num-bigint", + "phf", + "rustc-hash", + "seq-macro", "serde", + "smartstring", + "stacker", "swc_atoms", "swc_common", "swc_ecma_ast", - "swc_ecma_lexer", "tracing", ] @@ -1300,7 +1296,7 @@ checksum = "c16ce73424a6316e95e09065ba6a207eba7765496fed113702278b7711d4b632" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.114", ] [[package]] @@ -1311,7 +1307,7 @@ checksum = "aae1efbaa74943dc5ad2a2fb16cbd78b77d7e4d63188f3c5b4df2b4dcd2faaae" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.114", ] [[package]] @@ -1337,9 +1333,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.90" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ "proc-macro2", "quote", @@ -1348,13 +1344,13 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.114", ] [[package]] @@ -1383,29 +1379,29 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.114", ] [[package]] name = "time" -version = "0.3.36" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", "itoa", @@ -1418,15 +1414,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", @@ -1434,9 +1430,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "zerovec", @@ -1444,19 +1440,18 @@ dependencies = [ [[package]] name = "tokio" -version = "1.41.1" +version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ - "backtrace", "pin-project-lite", ] [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -1465,29 +1460,29 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.114", ] [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", ] [[package]] name = "triomphe" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8f7726da4807b58ea5c96fdc122f80702030edc33b35aff9190a51148ccc85" +checksum = "dd69c5aa8f924c7519d6372789a74eac5b94fb0f8fcf0d4a97eb0bfc3e785f39" dependencies = [ "serde", "stable_deref_trait", @@ -1524,7 +1519,7 @@ version = "12.0.1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.114", "termcolor", ] @@ -1536,40 +1531,29 @@ checksum = "81b79ad29b5e19de4260020f8919b443b2ef0277d242ce532ec7b7a2cc8b6007" [[package]] name = "unicode-ident" -version = "1.0.14" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unicode-width" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" - -[[package]] -name = "unicode-width" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "url" -version = "2.5.4" +version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", "idna", "percent-encoding", "serde", + "serde_derive", ] -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -1578,12 +1562,14 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.11.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" dependencies = [ - "getrandom", - "serde", + "getrandom 0.3.4", + "js-sys", + "serde_core", + "wasm-bindgen", ] [[package]] @@ -1594,41 +1580,37 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "wasm-bindgen" -version = "0.2.97" +name = "wasip2" +version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d15e63b4482863c109d70a7b8706c1e364eb6ea449b201a76c5b89cedcec2d5c" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ - "cfg-if", - "once_cell", - "wasm-bindgen-macro", + "wit-bindgen", ] [[package]] -name = "wasm-bindgen-backend" -version = "0.2.97" +name = "wasm-bindgen" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d36ef12e3aaca16ddd3f67922bc63e48e953f126de60bd33ccc0101ef9998cd" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ - "bumpalo", - "log", + "cfg-if", "once_cell", - "proc-macro2", - "quote", - "syn 2.0.90", + "rustversion", + "wasm-bindgen-macro", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.97" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "705440e08b42d3e4b36de7d66c944be628d579796b8090bfa3471478a2260051" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1636,39 +1618,92 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.97" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98c9ae5a76e46f4deecd0f0255cc223cfa18dc9b261213b8aa0c7b36f61b3f1d" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ + "bumpalo", "proc-macro2", "quote", - "syn 2.0.90", - "wasm-bindgen-backend", + "syn 2.0.114", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.97" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +dependencies = [ + "unicode-ident", +] [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys", + "windows-sys 0.61.2", ] [[package]] name = "windows-core" -version = "0.52.0" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "windows-targets", + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", ] [[package]] @@ -1680,6 +1715,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -1745,16 +1789,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "write16" -version = "1.0.0" +name = "wit-bindgen" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "writeable" -version = "0.5.5" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "wyz" @@ -1767,11 +1811,10 @@ dependencies = [ [[package]] name = "yoke" -version = "0.7.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -1779,63 +1822,73 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.114", "synstructure", ] [[package]] name = "zerocopy" -version = "0.7.35" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" dependencies = [ - "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.35" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.114", ] [[package]] name = "zerofrom" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.114", "synstructure", ] +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + [[package]] name = "zerovec" -version = "0.10.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ "yoke", "zerofrom", @@ -1844,11 +1897,17 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.3" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.114", ] + +[[package]] +name = "zmij" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac93432f5b761b22864c774aac244fa5c0fd877678a4c37ebf6cf42208f9c9ec" diff --git a/macros/src/types/enum.rs b/macros/src/types/enum.rs index 63ca62ed..b96a3b7a 100644 --- a/macros/src/types/enum.rs +++ b/macros/src/types/enum.rs @@ -1,6 +1,6 @@ use proc_macro2::TokenStream; use quote::quote; -use syn::{ext::IdentExt, Expr, Fields, ItemEnum, Variant}; +use syn::{ext::IdentExt, punctuated::Punctuated, token::Comma, Expr, Fields, ItemEnum, Variant}; use crate::{ attr::{Attr, EnumAttr, FieldAttr, Repr, StructAttr, Tagged, VariantAttr}, @@ -36,17 +36,18 @@ pub(crate) fn r#enum_def(s: &ItemEnum) -> syn::Result { let mut formatted_variants = Vec::new(); let mut formatted_optional_variants = Vec::new(); - let mut formatted_never = Vec::new(); let mut dependencies = Dependencies::new(crate_rename.clone()); + let (field_names, never_fields) = collect_field_names(&s.variants, &enum_attr)?; for (index, variant) in s.variants.iter().enumerate() { format_variant( &mut formatted_variants, &mut formatted_optional_variants, - &mut formatted_never, &mut dependencies, &enum_attr, variant, + &field_names[index], + &never_fields, index, )?; } @@ -62,11 +63,11 @@ pub(crate) fn r#enum_def(s: &ItemEnum) -> syn::Result { inline_flattened: enum_attr.repr.is_none().then_some(quote!( format!("({})", [#(#formatted_variants),*].join(" | ")) )), - optional_inline_flattened: if !formatted_optional_variants.is_empty() { + optional_inline_flattened: if formatted_optional_variants.len() == never_fields.len() { enum_attr.repr.is_none().then_some(quote!(format!( "({} | {{ {} }})", [#(#formatted_optional_variants),*].join(" | "), - [#(#formatted_never),*].join("; "), + [#(#never_fields),*].join("; "), ))) } else { None @@ -86,13 +87,15 @@ pub(crate) fn r#enum_def(s: &ItemEnum) -> syn::Result { fn format_variant( formatted_variants: &mut Vec, formatted_optional_variants: &mut Vec, - formatted_never: &mut Vec, dependencies: &mut Dependencies, enum_attr: &EnumAttr, variant: &Variant, - _index: usize, + ts_name: &Expr, + never_fields: &Vec, + index: usize, ) -> syn::Result<()> { let crate_rename = enum_attr.crate_rename(); + let never_fields_len = never_fields.len(); // If `variant.fields` is not a `Fields::Named(_)` the `rename_all_fields` // attribute must be ignored to prevent a `rename_all` from getting to @@ -106,16 +109,6 @@ fn format_variant( } let untagged_variant = variant_attr.untagged; - let ts_name = match (variant_attr.rename.clone(), &enum_attr.rename_all) { - (Some(rn), _) => rn, - (None, None) => { - make_string_literal(&variant.ident.unraw().to_string(), variant.ident.span()) - } - (None, Some(rn)) => make_string_literal( - &rn.apply(&variant.ident.unraw().to_string()), - variant.ident.span(), - ), - }; if let Some(ref repr) = enum_attr.repr { let formatted = match (repr, &variant.discriminant) { @@ -155,7 +148,6 @@ fn format_variant( } }; - let never_ty = quote!(format!("\"{}\"?: never", #ts_name)); let (formatted, formatted_optional) = match (untagged_variant, enum_attr.tagged()?) { (true, _) | (_, Tagged::Untagged) => (quote!(#parsed_ty), None), (false, Tagged::Externally) => match &variant.fields { @@ -169,27 +161,33 @@ fn format_variant( if field_attr.skip { (quote!(format!("\"{}\"", #ts_name)), None) } else { - let other_options = if !formatted_never.is_empty() { - // if let Some(past_option) = formatted_optional_variants.get_mut(index - 1) { - // quote!(#past_option.replace("}", format!("{}; }", #never_ty))); - // } - - quote!([#(#formatted_never),*].join("; ")) - } else { - quote!("") - }; - ( quote!(format!("{{ \"{}\": {} }}", #ts_name, #parsed_ty)), - Some(quote!( - format!("{{ \"{}\": {}; {}}}", #ts_name, #parsed_ty, #other_options) - )), + Some(quote!({ + if #never_fields_len >= 2 && #never_fields_len >= #index { + let mut never_field_options = vec![#(#never_fields),*]; + never_field_options.remove(#index); + + format!("{{ \"{}\": {}; {} }}", #ts_name, #parsed_ty, never_field_options.join("; ")) + } else { + format!("{{ \"{}\": {}; }}", #ts_name, #parsed_ty) + } + })), ) } } _ => ( quote!(format!("{{ \"{}\": {} }}", #ts_name, #parsed_ty)), - None, + Some(quote!({ + if #never_fields_len >= 2 && #never_fields_len >= #index { + let mut never_field_options = vec![#(#never_fields),*]; + never_field_options.remove(#index); + + format!("{{ \"{}\": {}; {} }}", #ts_name, #parsed_ty, never_field_options.join("; ")) + } else { + format!("{{ \"{}\": {}; }}", #ts_name, #parsed_ty) + } + })), ), }, (false, Tagged::Adjacently { tag, content }) => match &variant.fields { @@ -276,11 +274,45 @@ fn format_variant( formatted_variants.push(formatted); if let Some(formatted) = formatted_optional { formatted_optional_variants.push(formatted); - formatted_never.push(never_ty); } + Ok(()) } +fn collect_field_names( + variants: &Punctuated, + enum_attr: &EnumAttr, +) -> syn::Result<(Vec, Vec)> { + let mut field_names = Vec::new(); + let mut never_fields = Vec::new(); + + for variant in variants { + let variant_attr = VariantAttr::from_attrs(&variant.attrs)?; + + variant_attr.assert_validity(variant)?; + + if variant_attr.skip { + continue; + } + + let ts_name = match (variant_attr.rename.clone(), &enum_attr.rename_all) { + (Some(rn), _) => rn, + (None, None) => { + make_string_literal(&variant.ident.unraw().to_string(), variant.ident.span()) + } + (None, Some(rn)) => make_string_literal( + &rn.apply(&variant.ident.unraw().to_string()), + variant.ident.span(), + ), + }; + + field_names.push(ts_name.clone()); + never_fields.push(quote!(format!("\"{}\"?: never", #ts_name))); + } + + Ok((field_names, never_fields)) +} + // bindings for an empty enum (`never` in TS) fn empty_enum(ts_name: Expr, enum_attr: EnumAttr) -> DerivedTS { let crate_rename = enum_attr.crate_rename(); diff --git a/ts-rs/tests/integration/optional_flattened_enum.rs b/ts-rs/tests/integration/optional_flattened_enum.rs index b1a2f7d7..f47c427d 100644 --- a/ts-rs/tests/integration/optional_flattened_enum.rs +++ b/ts-rs/tests/integration/optional_flattened_enum.rs @@ -2,7 +2,7 @@ use serde::Serialize; use ts_rs::TS; #[test] -fn optional_flatten_enum_adds_empty_object() { +fn two_variant_enum() { #[derive(TS, Serialize)] #[serde(rename_all = "camelCase")] enum Enum { @@ -24,7 +24,31 @@ fn optional_flatten_enum_adds_empty_object() { } #[test] -fn optional_flatten_unit_variants_adds_empty_object() { +fn three_variant_enum() { + #[derive(TS, Serialize)] + #[serde(rename_all = "camelCase")] + enum Enum { + FirstOption(String), + SecondOption(bool), + ThirdOption(usize), + } + + #[derive(TS, Serialize)] + struct T { + a: String, + #[serde(flatten)] + flattened: Option, + } + + assert_eq!( + T::optional_inline_flattened(), + r#"{ a: string, } & ({ "firstOption": string; "secondOption"?: never; "thirdOption"?: never } | { "secondOption": boolean; "firstOption"?: never; "thirdOption"?: never } | { "thirdOption": number; "firstOption"?: never; "secondOption"?: never } | { "firstOption"?: never; "secondOption"?: never; "thirdOption"?: never })"# + ); +} + +#[test] +#[should_panic(expected = "Enum cannot be flattened")] +fn unit_variants() { #[derive(TS, Serialize)] #[serde(rename_all = "camelCase")] enum Enum { @@ -40,6 +64,7 @@ fn optional_flatten_unit_variants_adds_empty_object() { status: Option, } + // "first" | "second" | "third" isn't valid assert_eq!( T::optional_inline_flattened(), r#"{ a: string, } & ("first" | "second" | "third" | { "first"?: never; "second"?: never; "third"?: never })"# @@ -47,7 +72,8 @@ fn optional_flatten_unit_variants_adds_empty_object() { } #[test] -fn optional_flatten_mixed_variants_adds_empty_object() { +#[should_panic(expected = "Enum cannot be flattened")] +fn mixed_variant_types() { #[derive(TS, Serialize)] #[serde(rename_all = "camelCase")] enum Enum { @@ -63,14 +89,15 @@ fn optional_flatten_mixed_variants_adds_empty_object() { data: Option, } + // "unit" isn't valid assert_eq!( T::optional_inline_flattened(), - r#"{ a: string, } & ("unit" | { "tuple": [number, string]; "struct"?: never; "unit"?: never } | { "struct": { x: number, y: string, }; "tuple"?: never; "unit"?: never } | { "unit"?: never; "tuple"?: never; "struct"?: never })"# + r#"{ a: string, } & ("unit" | { "tuple": [number, string]; "unit"?: never; "struct"?: never } | { "struct": { x: number, y: string, }; "unit"?: never; "tuple"?: never } | { "unit"?: never; "tuple"?: never; "struct"?: never })"# ); } #[test] -fn optional_flatten_with_nested_objects_adds_empty_object() { +fn nested_structs() { #[derive(TS, Serialize)] struct Inner { value: i32, @@ -97,36 +124,7 @@ fn optional_flatten_with_nested_objects_adds_empty_object() { } #[test] -fn multiple_optional_flattened_enums_each_add_empty_object() { - #[derive(TS, Serialize)] - #[serde(rename_all = "camelCase")] - enum EnumA { - OptionA(String), - } - - #[derive(TS, Serialize)] - #[serde(rename_all = "camelCase")] - enum EnumB { - OptionB(i32), - } - - #[derive(TS, Serialize)] - struct T { - a: String, - #[serde(flatten)] - a_enum: Option, - #[serde(flatten)] - b_enum: Option, - } - - let result = T::optional_inline_flattened(); - - assert!(result.contains(r#"{ "optionA": string; "optionA"?: never } | { "optionA"?: never }"#)); - assert!(result.contains(r#"{ "optionB": number; "optionB"?: never } | { "optionB"?: never }"#)); -} - -#[test] -fn optional_flatten_with_rename_all_kebab_case() { +fn kebab_case_renaming() { #[derive(TS, Serialize)] #[serde(rename_all = "kebab-case")] enum Enum { @@ -150,7 +148,7 @@ fn optional_flatten_with_rename_all_kebab_case() { } #[test] -fn optional_flatten_single_variant_adds_empty_object() { +fn single_variant_enum() { #[derive(TS, Serialize)] #[serde(rename_all = "camelCase")] enum Enum { @@ -171,7 +169,7 @@ fn optional_flatten_single_variant_adds_empty_object() { } #[test] -fn non_optional_flatten_enum_is_unchanged() { +fn original_non_optional_enum() { #[derive(TS, Serialize)] #[serde(rename_all = "camelCase")] enum Enum { From da33c8c3e8387e8645843265ad2b8fd3760a5c28 Mon Sep 17 00:00:00 2001 From: 4dawit Date: Thu, 15 Jan 2026 16:57:52 -0600 Subject: [PATCH 5/6] tests: put optional_flattened_enum test behind serde-compat --- ts-rs/tests/integration/optional_flattened_enum.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ts-rs/tests/integration/optional_flattened_enum.rs b/ts-rs/tests/integration/optional_flattened_enum.rs index f47c427d..291ea6e6 100644 --- a/ts-rs/tests/integration/optional_flattened_enum.rs +++ b/ts-rs/tests/integration/optional_flattened_enum.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "serde-compat")] + use serde::Serialize; use ts_rs::TS; From c73f4b33042dd90d4a0872a4ba21d054ecbe8f19 Mon Sep 17 00:00:00 2001 From: 4dawit Date: Thu, 5 Mar 2026 14:22:55 -0600 Subject: [PATCH 6/6] fix: change usage of my original is_enum to new usage --- Cargo.lock | 6 +++++ README.md | 2 +- macros/src/lib.rs | 11 +++----- macros/src/types/enum.rs | 2 +- macros/src/types/unit.rs | 4 --- ts-rs/src/lib.rs | 26 +++++-------------- ts-rs/tests/integration/arrays.rs | 5 +++- ts-rs/tests/integration/bson.rs | 5 +++- ts-rs/tests/integration/concrete_generic.rs | 5 +++- .../integration/enum_variant_annotation.rs | 5 +++- ts-rs/tests/integration/generics_flatten.rs | 5 +++- ts-rs/tests/integration/list.rs | 5 +++- ts-rs/tests/integration/optional_field.rs | 10 +++++-- .../integration/optional_flattened_enum.rs | 18 ++++++------- ts-rs/tests/integration/repr_enum.rs | 20 +++----------- .../integration/serde_skip_with_default.rs | 5 +++- ts-rs/tests/integration/struct_rename.rs | 5 +++- ts-rs/tests/integration/type_override.rs | 5 +++- 18 files changed, 74 insertions(+), 70 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0e751aac..c28b30fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,6 +45,12 @@ dependencies = [ "object", ] +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "ast_node" version = "5.0.0" diff --git a/README.md b/README.md index 76f7ffff..8e0be433 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ Supported serde attributes: `rename`, `rename-all`, `rename-all-fields`, `tag`, from the generated type, but cannot use `#[serde(skip)]`, use `#[ts(skip)]` instead. When ts-rs encounters an unsupported serde attribute, a warning is emitted, unless the feature `no-serde-warnings` is enabled.\ -We are currently waiting for [#54140](https://github.com/rust-lang/rust/issues/54140), which will improve the ergonomics arund these diagnostics. +We are currently waiting for [#54140](https://github.com/rust-lang/rust/issues/54140), which will improve the ergonomics around these diagnostics. ### Cargo Features | **Feature** | **Description** | diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 815b6f36..f123dc9a 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -92,7 +92,6 @@ impl DerivedTS { let decl = self.generate_decl_fn(&rust_ty, &generics); let dependencies = &self.dependencies; let generics_fn = self.generate_generics_fn(&generics); - let is_enum = self.is_enum; quote! { #[automatically_derived] @@ -257,7 +256,7 @@ impl DerivedTS { let optional_inline_flattened = self.optional_inline_flattened.clone().unwrap_or_else(|| { quote! { - panic!("{} cannot be flattened", ::name()) + panic!("{} cannot be flattened", ::name(cfg)) } }); @@ -316,7 +315,7 @@ impl DerivedTS { #inline_flattened } - fn optional_inline_flattened() -> String { + fn optional_inline_flattened(cfg: &#crate_rename::Config) -> String { #optional_inline_flattened } } @@ -532,11 +531,7 @@ fn entry(input: proc_macro::TokenStream) -> Result { let input = syn::parse::(input)?; let (ts, ident, generics) = match input { Item::Struct(s) => (types::struct_def(&s)?, s.ident, s.generics), - Item::Enum(e) => { - let mut item_type = types::enum_def(&e)?; - item_type.is_enum = true; - (item_type, e.ident, e.generics) - } + Item::Enum(e) => (types::enum_def(&e)?, e.ident, e.generics), _ => syn_err!(input.span(); "unsupported item"), }; diff --git a/macros/src/types/enum.rs b/macros/src/types/enum.rs index b96a3b7a..3dd43d51 100644 --- a/macros/src/types/enum.rs +++ b/macros/src/types/enum.rs @@ -207,7 +207,7 @@ fn format_variant( } else { let ty = match field_attr.type_override { Some(type_override) => quote!(#type_override), - None => quote!(<#field_ty as #crate_rename::TS>::name(cfg)) + None => quote!(<#field_ty as #crate_rename::TS>::name(cfg)), }; ( diff --git a/macros/src/types/unit.rs b/macros/src/types/unit.rs index b8166135..001273f1 100644 --- a/macros/src/types/unit.rs +++ b/macros/src/types/unit.rs @@ -23,11 +23,7 @@ pub(crate) fn empty_object(attr: &StructAttr, ts_name: Expr) -> DerivedTS { concrete: attr.concrete.clone(), bound: attr.bound.clone(), ts_enum: None, -<<<<<<< HEAD is_enum: quote!(false), -======= - is_enum: false, ->>>>>>> d212ed6 (wip: added other options, need to retroactively add skipped past options) } } diff --git a/ts-rs/src/lib.rs b/ts-rs/src/lib.rs index 4337fddc..c44aba83 100644 --- a/ts-rs/src/lib.rs +++ b/ts-rs/src/lib.rs @@ -100,7 +100,7 @@ //! from the generated type, but cannot use `#[serde(skip)]`, use `#[ts(skip)]` instead. //! //! When ts-rs encounters an unsupported serde attribute, a warning is emitted, unless the feature `no-serde-warnings` is enabled.\ -//! We are currently waiting for [#54140](https://github.com/rust-lang/rust/issues/54140), which will improve the ergonomics arund these diagnostics. +//! We are currently waiting for [#54140](https://github.com/rust-lang/rust/issues/54140), which will improve the ergonomics around these diagnostics. //! //! ## Cargo Features //! | **Feature** | **Description** | @@ -393,9 +393,6 @@ pub trait TS { #[doc(hidden)] const IS_ENUM: bool = false; - #[doc(hidden)] - const IS_ENUM: bool = false; - /// JSDoc comment to describe this type in TypeScript - when `TS` is derived, docs are /// automatically read from your doc comments or `#[doc = ".."]` attributes fn docs() -> Option { @@ -404,7 +401,7 @@ pub trait TS { /// Identifier of this type, excluding generic parameters. fn ident(cfg: &Config) -> String { - // by default, fall back to `TS::name()`. + // by default, fall back to `TS::name(cfg)`. let name = ::name(cfg); match name.find('<') { @@ -446,7 +443,9 @@ pub trait TS { /// Flatten an optional type declaration. /// This function will panic if the type cannot be flattened. - fn optional_inline_flattened(cfg: &Config) -> String; + fn optional_inline_flattened(cfg: &Config) -> String { + panic!("{} cannot be flattened", Self::name(cfg)) + } /// Iterates over all dependency of this type. fn visit_dependencies(_: &mut impl TypeVisitor) @@ -737,7 +736,6 @@ macro_rules! impl_primitives { type OptionInnerType = Self; fn name(_: &$crate::Config) -> String { String::from($l) } fn inline(cfg: &$crate::Config) -> String { ::name(cfg) } - fn optional_inline_flattened(cfg: &$crate::Config) -> String { ::name(cfg) } } )*)* }; } @@ -776,9 +774,9 @@ macro_rules! impl_tuples { )* } fn inline_flattened(_: &$crate::Config) -> String { panic!("tuple cannot be flattened") } + fn optional_inline_flattened(_: &$crate::Config) -> String { panic!("tuple cannot be flattened") } fn decl(_: &$crate::Config) -> String { panic!("tuple cannot be declared") } fn decl_concrete(_: &$crate::Config) -> String { panic!("tuple cannot be declared") } - fn optional_inline_flattened(_: &$crate::Config) -> String { panic!("tuple cannot be declared") } } }; ( $i2:ident $(, $i:ident)* ) => { @@ -1053,10 +1051,6 @@ impl TS for HashMap { fn inline_flattened(cfg: &Config) -> String { format!("({})", Self::inline(cfg)) } - - fn optional_inline_flattened() -> String { - panic!("{} cannot be flattened", ::name()) - } } // TODO: replace manual impl with dummy struct & `impl_shadow` (like for `JsonValue`) @@ -1087,10 +1081,6 @@ impl TS for Range { fn inline(cfg: &Config) -> String { panic!("{} cannot be inlined", Self::name(cfg)) } - - fn optional_inline_flattened() -> String { - panic!("{} cannot be flattened", ::name()) - } } impl_shadow!(as Range: impl TS for RangeInclusive); @@ -1210,10 +1200,6 @@ impl TS for Dummy { fn inline(cfg: &Config) -> String { panic!("{} cannot be inlined", Self::name(cfg)) } - - fn optional_inline_flattened() -> String { - panic!("{} cannot be flattened", ::name()) - } } /// Formats rust doc comments, turning them into a JSDoc comments. diff --git a/ts-rs/tests/integration/arrays.rs b/ts-rs/tests/integration/arrays.rs index 9f721cae..a164b51a 100644 --- a/ts-rs/tests/integration/arrays.rs +++ b/ts-rs/tests/integration/arrays.rs @@ -11,7 +11,10 @@ struct Interface { #[test] fn free() { let cfg = Config::from_env(); - assert_eq!(<[String; 4]>::inline(&cfg), "[string, string, string, string]") + assert_eq!( + <[String; 4]>::inline(&cfg), + "[string, string, string, string]" + ) } #[test] diff --git a/ts-rs/tests/integration/bson.rs b/ts-rs/tests/integration/bson.rs index 95005ec6..588e9958 100644 --- a/ts-rs/tests/integration/bson.rs +++ b/ts-rs/tests/integration/bson.rs @@ -13,5 +13,8 @@ struct User { #[test] fn bson() { let cfg = Config::from_env(); - assert_eq!(User::decl(&cfg), "type User = { _id: string, _uuid: string, };") + assert_eq!( + User::decl(&cfg), + "type User = { _id: string, _uuid: string, };" + ) } diff --git a/ts-rs/tests/integration/concrete_generic.rs b/ts-rs/tests/integration/concrete_generic.rs index d06de7ae..2b304da8 100644 --- a/ts-rs/tests/integration/concrete_generic.rs +++ b/ts-rs/tests/integration/concrete_generic.rs @@ -84,7 +84,10 @@ mod simple { #[test] fn simple() { let cfg = Config::from_env(); - assert_eq!(Simple::::decl(&cfg), "type Simple = { t: number, };"); + assert_eq!( + Simple::::decl(&cfg), + "type Simple = { t: number, };" + ); assert_eq!( WithOption::::decl(&cfg), "type WithOption = { opt: number | null, };" diff --git a/ts-rs/tests/integration/enum_variant_annotation.rs b/ts-rs/tests/integration/enum_variant_annotation.rs index 1bd43894..6a302e59 100644 --- a/ts-rs/tests/integration/enum_variant_annotation.rs +++ b/ts-rs/tests/integration/enum_variant_annotation.rs @@ -75,7 +75,10 @@ pub enum C { #[test] fn test_enum_variant_with_tag() { let cfg = Config::from_env(); - assert_eq!(C::inline(&cfg), r#"{ "kind": "SQUARE_THING", name: string, }"#); + assert_eq!( + C::inline(&cfg), + r#"{ "kind": "SQUARE_THING", name: string, }"# + ); } #[cfg(feature = "serde-compat")] diff --git a/ts-rs/tests/integration/generics_flatten.rs b/ts-rs/tests/integration/generics_flatten.rs index 254648e2..818db377 100644 --- a/ts-rs/tests/integration/generics_flatten.rs +++ b/ts-rs/tests/integration/generics_flatten.rs @@ -45,7 +45,10 @@ fn flattened_generic_parameters() { } let cfg = Config::from_env(); - assert_eq!(Item::<()>::decl(&cfg), "type Item = { id: string, } & D;"); + assert_eq!( + Item::<()>::decl(&cfg), + "type Item = { id: string, } & D;" + ); assert_eq!( TwoParameters::<(), ()>::decl(&cfg), "type TwoParameters = { id: string, ab: [A, B], } & A & B;" diff --git a/ts-rs/tests/integration/list.rs b/ts-rs/tests/integration/list.rs index 1ce870c9..65c2b400 100644 --- a/ts-rs/tests/integration/list.rs +++ b/ts-rs/tests/integration/list.rs @@ -10,5 +10,8 @@ struct List { #[test] fn list() { let cfg = Config::from_env(); - assert_eq!(List::decl(&cfg), "type List = { data: Array | null, };"); + assert_eq!( + List::decl(&cfg), + "type List = { data: Array | null, };" + ); } diff --git a/ts-rs/tests/integration/optional_field.rs b/ts-rs/tests/integration/optional_field.rs index 0ad7bbe5..569c3525 100644 --- a/ts-rs/tests/integration/optional_field.rs +++ b/ts-rs/tests/integration/optional_field.rs @@ -19,7 +19,10 @@ fn in_struct() { let b = "b?: number | null"; let c = "c: number | null"; let cfg = Config::from_env(); - assert_eq!(OptionalInStruct::inline(&cfg), format!("{{ {a}, {b}, {c}, }}")); + assert_eq!( + OptionalInStruct::inline(&cfg), + format!("{{ {a}, {b}, {c}, }}") + ); } #[derive(Serialize, TS)] @@ -105,7 +108,10 @@ fn inline() { let b = "b?: number | null"; let c = "c: number | null"; let cfg = Config::from_env(); - assert_eq!(Inline::inline(&cfg), format!("{{ x: {{ {a}, {b}, {c}, }}, }}")); + assert_eq!( + Inline::inline(&cfg), + format!("{{ x: {{ {a}, {b}, {c}, }}, }}") + ); } type Foo = Option; diff --git a/ts-rs/tests/integration/optional_flattened_enum.rs b/ts-rs/tests/integration/optional_flattened_enum.rs index 291ea6e6..02d9ba74 100644 --- a/ts-rs/tests/integration/optional_flattened_enum.rs +++ b/ts-rs/tests/integration/optional_flattened_enum.rs @@ -1,7 +1,7 @@ #![cfg(feature = "serde-compat")] use serde::Serialize; -use ts_rs::TS; +use ts_rs::{Config, TS}; #[test] fn two_variant_enum() { @@ -20,7 +20,7 @@ fn two_variant_enum() { } assert_eq!( - T::optional_inline_flattened(), + T::optional_inline_flattened(&Config::default()), r#"{ a: string, } & ({ "firstOption": string; "secondOption"?: never } | { "secondOption": boolean; "firstOption"?: never } | { "firstOption"?: never; "secondOption"?: never })"# ); } @@ -43,7 +43,7 @@ fn three_variant_enum() { } assert_eq!( - T::optional_inline_flattened(), + T::optional_inline_flattened(&Config::default()), r#"{ a: string, } & ({ "firstOption": string; "secondOption"?: never; "thirdOption"?: never } | { "secondOption": boolean; "firstOption"?: never; "thirdOption"?: never } | { "thirdOption": number; "firstOption"?: never; "secondOption"?: never } | { "firstOption"?: never; "secondOption"?: never; "thirdOption"?: never })"# ); } @@ -68,7 +68,7 @@ fn unit_variants() { // "first" | "second" | "third" isn't valid assert_eq!( - T::optional_inline_flattened(), + T::optional_inline_flattened(&Config::default()), r#"{ a: string, } & ("first" | "second" | "third" | { "first"?: never; "second"?: never; "third"?: never })"# ); } @@ -93,7 +93,7 @@ fn mixed_variant_types() { // "unit" isn't valid assert_eq!( - T::optional_inline_flattened(), + T::optional_inline_flattened(&Config::default()), r#"{ a: string, } & ("unit" | { "tuple": [number, string]; "unit"?: never; "struct"?: never } | { "struct": { x: number, y: string, }; "unit"?: never; "tuple"?: never } | { "unit"?: never; "tuple"?: never; "struct"?: never })"# ); } @@ -120,7 +120,7 @@ fn nested_structs() { } assert_eq!( - T::optional_inline_flattened(), + T::optional_inline_flattened(&Config::default()), r#"{ a: string, } & ({ "first": Inner; "second"?: never } | { "second": Inner; "first"?: never } | { "first"?: never; "second"?: never })"# ); } @@ -141,7 +141,7 @@ fn kebab_case_renaming() { flattened: Option, } - let result = T::optional_inline_flattened(); + let result = T::optional_inline_flattened(&Config::default()); assert!(result.contains(r#""first-option": string"#)); assert!(result.contains(r#""second-option": boolean"#)); @@ -165,7 +165,7 @@ fn single_variant_enum() { } assert_eq!( - T::optional_inline_flattened(), + T::optional_inline_flattened(&Config::default()), r#"{ a: string, } & ({ "only": string; } | { "only"?: never })"# ); } @@ -187,7 +187,7 @@ fn original_non_optional_enum() { } assert_eq!( - T::optional_inline_flattened(), + T::optional_inline_flattened(&Config::default()), r#"{ a: string, } & ({ "firstOption": string } | { "secondOption": boolean })"# ); } diff --git a/ts-rs/tests/integration/repr_enum.rs b/ts-rs/tests/integration/repr_enum.rs index 7ba92d48..39c71bba 100644 --- a/ts-rs/tests/integration/repr_enum.rs +++ b/ts-rs/tests/integration/repr_enum.rs @@ -53,22 +53,10 @@ enum KebabCase { #[test] fn native_ts_enum_repr() { let cfg = Config::from_env(); - assert_eq!( - Foo::decl(&cfg), - "enum Foo { \"A\" = 1, \"B\" = 2 }" - ); - assert_eq!( - Bar::decl(&cfg), - "enum Bar { \"A\" = 1, \"B\" }" - ); - assert_eq!( - Baz::decl(&cfg), - "enum Baz { \"A\", \"B\" }" - ); - assert_eq!( - Biz::decl(&cfg), - "enum Biz { \"A\" = \"A\", \"B\" = \"B\" }" - ); + assert_eq!(Foo::decl(&cfg), "enum Foo { \"A\" = 1, \"B\" = 2 }"); + assert_eq!(Bar::decl(&cfg), "enum Bar { \"A\" = 1, \"B\" }"); + assert_eq!(Baz::decl(&cfg), "enum Baz { \"A\", \"B\" }"); + assert_eq!(Biz::decl(&cfg), "enum Biz { \"A\" = \"A\", \"B\" = \"B\" }"); assert_eq!( SnakeCase::decl(&cfg), "enum SnakeCase { \"enum_variant_foo\" = \"enum_variant_foo\", \"enum_variant_bar\" = \"enum_variant_bar\" }" diff --git a/ts-rs/tests/integration/serde_skip_with_default.rs b/ts-rs/tests/integration/serde_skip_with_default.rs index 9bffb57b..75ddceae 100644 --- a/ts-rs/tests/integration/serde_skip_with_default.rs +++ b/ts-rs/tests/integration/serde_skip_with_default.rs @@ -22,5 +22,8 @@ pub struct Foobar { #[test] fn serde_skip_with_default() { let cfg = Config::from_env(); - assert_eq!(Foobar::decl(&cfg), "type Foobar = { something_else: number, };"); + assert_eq!( + Foobar::decl(&cfg), + "type Foobar = { something_else: number, };" + ); } diff --git a/ts-rs/tests/integration/struct_rename.rs b/ts-rs/tests/integration/struct_rename.rs index e1f87b04..97a9f91c 100644 --- a/ts-rs/tests/integration/struct_rename.rs +++ b/ts-rs/tests/integration/struct_rename.rs @@ -80,7 +80,10 @@ struct RenameSerdeSpecialChar { #[test] fn serde_rename_special_char() { let cfg = Config::from_env(); - assert_eq!(RenameSerdeSpecialChar::inline(&cfg), r#"{ "a/b": number, }"#); + assert_eq!( + RenameSerdeSpecialChar::inline(&cfg), + r#"{ "a/b": number, }"# + ); } // struct-level renames diff --git a/ts-rs/tests/integration/type_override.rs b/ts-rs/tests/integration/type_override.rs index 23603305..2308aad1 100644 --- a/ts-rs/tests/integration/type_override.rs +++ b/ts-rs/tests/integration/type_override.rs @@ -73,5 +73,8 @@ fn enum_newtype_representations() { // regression test for https://github.com/Aleph-Alpha/ts-rs/issues/126 let cfg = Config::from_env(); assert_eq!(Internal::inline(&cfg), r#"{ "t": "Newtype" } & unknown"#); - assert_eq!(Adjacent::inline(&cfg), r#"{ "t": "Newtype", "c": unknown }"#); + assert_eq!( + Adjacent::inline(&cfg), + r#"{ "t": "Newtype", "c": unknown }"# + ); }