1#![cfg_attr(docsrs, feature(doc_cfg))]
97#![doc(
98 html_logo_url = "https://github.com/specta-rs/specta/raw/main/.github/logo-128.png",
99 html_favicon_url = "https://github.com/specta-rs/specta/raw/main/.github/logo-128.png"
100)]
101
102use std::{
103 borrow::Cow,
104 collections::{HashMap, HashSet, VecDeque},
105};
106
107use specta::{
108 FormatError, Types,
109 datatype::{
110 DataType, Enum, Field, Fields, NamedDataType, NamedReference, NamedReferenceType,
111 Primitive, Reference, Struct, Tuple, UnnamedFields, Variant,
112 },
113};
114
115mod error;
116mod inflection;
117mod parser;
118mod phased;
119mod repr;
120mod validate;
121
122use inflection::RenameRule;
123use parser::{SerdeContainerAttrs, SerdeFieldAttrs, SerdeVariantAttrs};
124use phased::PhasedTy;
125use repr::EnumRepr;
126
127pub use error::Error;
128pub use phased::{Phased, phased};
129
130#[derive(Debug, Clone, Copy, PartialEq, Eq)]
132pub enum Phase {
133 Serialize,
135 Deserialize,
137}
138
139pub struct Format;
154
155impl specta::Format for Format {
156 fn map_types(&'_ self, types: &Types) -> Result<Cow<'_, Types>, FormatError> {
157 validate::validate_for_mode(types, validate::ApplyMode::Unified)?;
158
159 let mut out = types.clone();
160 let generated = HashMap::<TypeIdentity, SplitGeneratedTypes>::new();
161 let split_types = HashSet::<TypeIdentity>::new();
162 let mut rewrite_err = None;
163
164 out.iter_mut(|ndt| {
165 if rewrite_err.is_some() {
166 return;
167 }
168
169 let ndt_name = ndt.name.to_string();
170
171 if let Some(ty) = ndt.ty.as_mut() {
172 if rewrite_err.is_some() {
173 return;
174 }
175
176 if let Err(err) = rewrite_datatype_for_phase(
177 ty,
178 PhaseRewrite::Unified,
179 types,
180 &generated,
181 &split_types,
182 Some(ndt_name.as_str()),
183 ) {
184 rewrite_err = Some(err);
185 }
186 }
187
188 if rewrite_err.is_some() {
189 return;
190 }
191
192 if let Err(err) = rewrite_named_type_for_phase(ndt, PhaseRewrite::Unified) {
193 rewrite_err = Some(err);
194 }
195 });
196
197 if let Some(err) = rewrite_err {
198 return Err(Box::new(err));
199 }
200
201 Ok(Cow::Owned(out))
202 }
203
204 fn map_type(&'_ self, types: &Types, dt: &DataType) -> Result<Cow<'_, DataType>, FormatError> {
205 if datatype_is_registered_definition(types, dt) {
206 return Ok(Cow::Owned(dt.clone()));
207 }
208
209 validate::validate_datatype_for_mode(dt, types, validate::ApplyMode::Unified)?;
210
211 let mut dt = dt.clone();
212 rewrite_datatype_for_phase(
213 &mut dt,
214 PhaseRewrite::Unified,
215 types,
216 &HashMap::new(),
217 &HashSet::new(),
218 None,
219 )?;
220
221 Ok(Cow::Owned(dt))
222 }
223}
224
225pub struct PhasesFormat;
242
243impl specta::Format for PhasesFormat {
244 fn map_types(&'_ self, types: &Types) -> Result<Cow<'_, Types>, FormatError> {
245 validate::validate_for_mode(types, validate::ApplyMode::Phases)?;
246
247 let originals = types.into_unsorted_iter().collect::<Vec<_>>();
248 let mut dependencies = HashMap::<TypeIdentity, HashSet<TypeIdentity>>::new();
249 let mut reverse_dependencies = HashMap::<TypeIdentity, HashSet<TypeIdentity>>::new();
250
251 for original in &originals {
252 let key = TypeIdentity::from_ndt(original);
253 let mut deps = HashSet::new();
254 if let Some(ty) = &original.ty {
255 collect_dependencies(ty, types, &mut deps)?;
256 }
257 for dep in &deps {
258 reverse_dependencies
259 .entry(dep.clone())
260 .or_default()
261 .insert(key.clone());
262 }
263 dependencies.insert(key, deps);
264 }
265
266 let mut split_types = HashSet::new();
267 for ndt in &originals {
268 if ndt
269 .ty
270 .as_ref()
271 .is_some_and(|ty| has_local_phase_difference(ty).unwrap_or(false))
272 {
273 split_types.insert(TypeIdentity::from_ndt(ndt));
274 }
275 }
276
277 let mut queue = VecDeque::from_iter(split_types.iter().cloned());
278 while let Some(key) = queue.pop_front() {
279 if let Some(dependents) = reverse_dependencies.get(&key) {
280 for dependent in dependents {
281 if split_types.insert(dependent.clone()) {
282 queue.push_back(dependent.clone());
283 }
284 }
285 }
286 }
287
288 let mut out = types.clone();
289 let mut generated = HashMap::<TypeIdentity, SplitGeneratedTypes>::new();
290 let mut generated_types = HashSet::<TypeIdentity>::new();
291
292 for original in &originals {
293 let key = TypeIdentity::from_ndt(original);
294
295 if split_types.contains(&key) {
296 let serialize_ndt = build_from_original(original, PhaseRewrite::Serialize)?;
297
298 let deserialize_ndt = build_from_original(original, PhaseRewrite::Deserialize)?;
299
300 generated.insert(
301 key,
302 SplitGeneratedTypes {
303 serialize: serialize_ndt,
304 deserialize: Box::new(deserialize_ndt),
305 },
306 );
307 }
308 }
309
310 for original in &originals {
311 let key = TypeIdentity::from_ndt(original);
312
313 if !split_types.contains(&key) {
314 continue;
315 }
316
317 let Some(mut generated_types_for_phase) = generated.get(&key).cloned() else {
318 continue;
319 };
320
321 let mut rewrite_err = None;
322 if let Some(ty) = generated_types_for_phase.serialize.ty.as_mut()
323 && let Err(err) = rewrite_datatype_for_phase(
324 ty,
325 PhaseRewrite::Serialize,
326 types,
327 &generated,
328 &split_types,
329 Some(original.name.as_ref()),
330 )
331 {
332 rewrite_err = Some(err);
333 }
334 if let Some(err) = rewrite_err.take() {
335 return Err(Box::new(err));
336 }
337
338 rewrite_named_type_for_phase(
339 &mut generated_types_for_phase.serialize,
340 PhaseRewrite::Serialize,
341 )?;
342
343 if let Some(ty) = generated_types_for_phase.deserialize.ty.as_mut()
344 && let Err(err) = rewrite_datatype_for_phase(
345 ty,
346 PhaseRewrite::Deserialize,
347 types,
348 &generated,
349 &split_types,
350 Some(original.name.as_ref()),
351 )
352 {
353 rewrite_err = Some(err);
354 }
355 if let Some(err) = rewrite_err {
356 return Err(Box::new(err));
357 }
358
359 rewrite_named_type_for_phase(
360 &mut generated_types_for_phase.deserialize,
361 PhaseRewrite::Deserialize,
362 )?;
363
364 generated.insert(key, generated_types_for_phase);
365 }
366
367 for generated_types_for_phase in generated.values_mut() {
368 let serialize =
369 register_generated_type(&mut out, generated_types_for_phase.serialize.clone());
370 let deserialize = Box::new(register_generated_type(
371 &mut out,
372 (*generated_types_for_phase.deserialize).clone(),
373 ));
374
375 generated_types.insert(TypeIdentity::from_ndt(&serialize));
376 generated_types.insert(TypeIdentity::from_ndt(&deserialize));
377
378 generated_types_for_phase.serialize = serialize;
379 generated_types_for_phase.deserialize = deserialize;
380 }
381
382 let registered_generated = generated.clone();
383 for generated_types_for_phase in generated.values_mut() {
384 if let Some(ty) = generated_types_for_phase.serialize.ty.as_mut() {
385 rewrite_datatype_for_phase(
386 ty,
387 PhaseRewrite::Serialize,
388 types,
389 ®istered_generated,
390 &split_types,
391 Some(generated_types_for_phase.serialize.name.as_ref()),
392 )?;
393 }
394
395 if let Some(ty) = generated_types_for_phase.deserialize.ty.as_mut() {
396 rewrite_datatype_for_phase(
397 ty,
398 PhaseRewrite::Deserialize,
399 types,
400 ®istered_generated,
401 &split_types,
402 Some(generated_types_for_phase.deserialize.name.as_ref()),
403 )?;
404 }
405 }
406
407 out.iter_mut(|ndt| {
408 for generated_types_for_phase in generated.values() {
409 if ndt.name == generated_types_for_phase.serialize.name {
410 ndt.ty = generated_types_for_phase.serialize.ty.clone();
411 return;
412 }
413
414 if ndt.name == generated_types_for_phase.deserialize.name {
415 ndt.ty = generated_types_for_phase.deserialize.ty.clone();
416 return;
417 }
418 }
419 });
420
421 let mut rewrite_err = None;
422 out.iter_mut(|ndt| {
423 if rewrite_err.is_some() {
424 return;
425 }
426
427 let ndt_name = ndt.name.to_string();
428 let key = TypeIdentity::from_ndt(ndt);
429
430 if split_types.contains(&key) || generated_types.contains(&key) {
431 return;
432 }
433
434 if let Some(ty) = ndt.ty.as_mut()
435 && let Err(err) = rewrite_datatype_for_phase(
436 ty,
437 PhaseRewrite::Unified,
438 types,
439 &generated,
440 &split_types,
441 Some(ndt_name.as_str()),
442 )
443 {
444 rewrite_err = Some(err);
445 return;
446 }
447
448 if let Err(err) = rewrite_named_type_for_phase(ndt, PhaseRewrite::Unified) {
449 rewrite_err = Some(err);
450 }
451 });
452
453 if let Some(err) = rewrite_err {
454 return Err(Box::new(err));
455 }
456
457 out.iter_mut(|ndt| {
458 let key = TypeIdentity::from_ndt(ndt);
459 if !split_types.contains(&key) {
460 return;
461 }
462
463 let Some(SplitGeneratedTypes {
464 serialize,
465 deserialize,
466 }) = generated.get(&key)
467 else {
468 return;
469 };
470
471 let generic_args = ndt
472 .generics
473 .iter()
474 .map(|generic| {
475 let generic = specta::datatype::Generic::new(generic.name.clone());
476 (generic.clone(), generic.into())
477 })
478 .collect::<Vec<_>>();
479
480 let mut serialize_variant = Variant::unnamed().build();
481 if let Fields::Unnamed(fields) = &mut serialize_variant.fields {
482 fields
483 .fields
484 .push(Field::new(serialize.reference(generic_args.clone()).into()));
485 }
486
487 let mut deserialize_variant = Variant::unnamed().build();
488 if let Fields::Unnamed(fields) = &mut deserialize_variant.fields {
489 fields
490 .fields
491 .push(Field::new(deserialize.reference(generic_args).into()));
492 }
493
494 let mut wrapper = Enum::default();
495 wrapper
496 .variants
497 .push((Cow::Borrowed("Serialize"), serialize_variant));
498 wrapper
499 .variants
500 .push((Cow::Borrowed("Deserialize"), deserialize_variant));
501
502 ndt.ty = Some(DataType::Enum(wrapper));
503 });
504 Ok(Cow::Owned(out))
505 }
506
507 fn map_type(&'_ self, types: &Types, dt: &DataType) -> Result<Cow<'_, DataType>, FormatError> {
508 if datatype_is_registered_definition(types, dt) {
509 return Ok(Cow::Owned(dt.clone()));
510 }
511
512 let mut selected = select_phase_datatype(dt, types, Phase::Serialize);
513
514 validate::validate_datatype_for_mode_shallow(
515 &selected,
516 types,
517 validate::ApplyMode::Phases,
518 )?;
519
520 rewrite_datatype_for_phase(
521 &mut selected,
522 PhaseRewrite::Serialize,
523 types,
524 &HashMap::new(),
525 &HashSet::new(),
526 None,
527 )?;
528
529 Ok(Cow::Owned(selected))
530 }
531}
532
533fn datatype_is_registered_definition(types: &Types, dt: &DataType) -> bool {
534 types
535 .into_unsorted_iter()
536 .any(|ndt| ndt.ty.as_ref() == Some(dt))
537}
538
539pub fn select_phase_datatype(dt: &DataType, types: &Types, phase: Phase) -> DataType {
594 let mut dt = dt.clone();
595 select_phase_datatype_inner(&mut dt, types, phase);
596 dt
597}
598
599#[derive(Debug, Clone, Copy, PartialEq, Eq)]
600enum PhaseRewrite {
601 Unified,
602 Serialize,
603 Deserialize,
604}
605
606fn select_phase_datatype_inner(ty: &mut DataType, types: &Types, phase: Phase) {
607 if let Some(resolved) = select_split_wrapper_variant(ty, phase) {
608 *ty = resolved;
609 select_phase_datatype_inner(ty, types, phase);
610 return;
611 }
612
613 if let Some(resolved) = select_explicit_phased_type(ty, phase) {
614 *ty = resolved;
615 select_phase_datatype_inner(ty, types, phase);
616 return;
617 }
618
619 match ty {
620 DataType::Struct(s) => select_phase_fields(&mut s.fields, types, phase),
621 DataType::Enum(e) => {
622 for (_, variant) in &mut e.variants {
623 select_phase_fields(&mut variant.fields, types, phase);
624 }
625 }
626 DataType::Tuple(tuple) => {
627 for ty in &mut tuple.elements {
628 select_phase_datatype_inner(ty, types, phase);
629 }
630 }
631 DataType::List(list) => select_phase_datatype_inner(&mut list.ty, types, phase),
632 DataType::Map(map) => {
633 select_phase_datatype_inner(map.key_ty_mut(), types, phase);
634 select_phase_datatype_inner(map.value_ty_mut(), types, phase);
635 }
636 DataType::Intersection(types_) => {
637 for ty in types_ {
638 select_phase_datatype_inner(ty, types, phase);
639 }
640 }
641 DataType::Nullable(inner) => select_phase_datatype_inner(inner, types, phase),
642 DataType::Reference(Reference::Named(reference)) => {
643 if let NamedReferenceType::Inline { dt, .. } = &mut reference.inner {
644 select_phase_datatype_inner(dt, types, phase);
645 return;
646 }
647
648 let Some(referenced_ndt) = types.get(reference) else {
649 return;
650 };
651 for (_, dt) in named_reference_generics_mut(reference) {
652 select_phase_datatype_inner(dt, types, phase);
653 }
654
655 if let Some(mut selected) = referenced_ndt
656 .ty
657 .as_ref()
658 .and_then(|ty| select_split_wrapper_variant(ty, phase))
659 {
660 select_phase_datatype_inner(&mut selected, types, phase);
661 *ty = selected;
662 return;
663 }
664
665 let target_ndt =
666 select_split_type_variant(referenced_ndt, types, phase).unwrap_or(referenced_ndt);
667 let Reference::Named(new_reference) =
668 target_ndt.reference(named_reference_generics(reference).to_vec())
669 else {
670 unreachable!("named types always produce named references")
671 };
672 *reference = new_reference;
673 }
674 DataType::Reference(Reference::Opaque(_))
675 | DataType::Generic(_)
676 | DataType::Primitive(_) => {}
677 }
678}
679
680fn select_phase_fields(fields: &mut Fields, types: &Types, phase: Phase) {
681 match fields {
682 Fields::Unit => {}
683 Fields::Unnamed(fields) => {
684 for field in &mut fields.fields {
685 if let Some(ty) = field.ty.as_mut() {
686 select_phase_datatype_inner(ty, types, phase);
687 }
688 }
689 }
690 Fields::Named(fields) => {
691 for (_, field) in &mut fields.fields {
692 if let Some(ty) = field.ty.as_mut() {
693 select_phase_datatype_inner(ty, types, phase);
694 }
695 }
696 }
697 }
698}
699
700fn select_explicit_phased_type(ty: &DataType, phase: Phase) -> Option<DataType> {
701 let DataType::Reference(Reference::Opaque(reference)) = ty else {
702 return None;
703 };
704 let phased = reference.downcast_ref::<PhasedTy>()?;
705
706 Some(match phase {
707 Phase::Serialize => phased.serialize.clone(),
708 Phase::Deserialize => phased.deserialize.clone(),
709 })
710}
711
712fn select_split_wrapper_variant(ty: &DataType, phase: Phase) -> Option<DataType> {
713 let DataType::Enum(wrapper) = ty else {
714 return None;
715 };
716
717 if wrapper.variants.len() != 2 {
718 return None;
719 }
720
721 let variant_name = match phase {
722 Phase::Serialize => "Serialize",
723 Phase::Deserialize => "Deserialize",
724 };
725
726 let (_, variant) = wrapper
727 .variants
728 .iter()
729 .find(|(name, _)| name == variant_name)?;
730 let Fields::Unnamed(fields) = &variant.fields else {
731 return None;
732 };
733
734 let [field] = &fields.fields[..] else {
735 return None;
736 };
737
738 field.ty.clone()
739}
740
741fn select_split_type_variant<'a>(
742 ndt: &'a NamedDataType,
743 types: &'a Types,
744 phase: Phase,
745) -> Option<&'a NamedDataType> {
746 let Some(DataType::Enum(wrapper)) = &ndt.ty else {
747 return None;
748 };
749
750 if wrapper.variants.len() != 2 {
751 return None;
752 }
753
754 let variant_name = match phase {
755 Phase::Serialize => "Serialize",
756 Phase::Deserialize => "Deserialize",
757 };
758
759 let (_, variant) = wrapper
760 .variants
761 .iter()
762 .find(|(name, _)| name == variant_name)?;
763 let Fields::Unnamed(fields) = &variant.fields else {
764 return None;
765 };
766 let [field] = &fields.fields[..] else {
767 return None;
768 };
769 let Some(DataType::Reference(Reference::Named(reference))) = field.ty.as_ref() else {
770 return None;
771 };
772
773 types.get(reference)
774}
775
776fn named_reference_generics(
777 reference: &NamedReference,
778) -> &[(specta::datatype::Generic, DataType)] {
779 match &reference.inner {
780 NamedReferenceType::Reference { generics, .. } => generics,
781 NamedReferenceType::Inline { .. } | NamedReferenceType::Recursive(_) => &[],
782 }
783}
784
785fn named_reference_generics_mut(
786 reference: &mut NamedReference,
787) -> &mut [(specta::datatype::Generic, DataType)] {
788 match &mut reference.inner {
789 NamedReferenceType::Reference { generics, .. } => generics,
790 NamedReferenceType::Inline { .. } | NamedReferenceType::Recursive(_) => &mut [],
791 }
792}
793
794#[derive(Debug, Clone)]
795struct SplitGeneratedTypes {
796 serialize: NamedDataType,
797 deserialize: Box<NamedDataType>,
798}
799
800#[derive(Debug, Clone, PartialEq, Eq, Hash)]
801struct TypeIdentity {
802 name: String,
803 module_path: String,
804 file: &'static str,
805 line: u32,
806 column: u32,
807}
808
809impl TypeIdentity {
810 fn from_ndt(ty: &specta::datatype::NamedDataType) -> Self {
811 let location = ty.location;
812 Self {
813 name: ty.name.to_string(),
814 module_path: ty.module_path.to_string(),
815 file: location.file(),
816 line: location.line(),
817 column: location.column(),
818 }
819 }
820}
821
822fn rewrite_datatype_for_phase(
823 ty: &mut DataType,
824 mode: PhaseRewrite,
825 original_types: &Types,
826 generated: &HashMap<TypeIdentity, SplitGeneratedTypes>,
827 split_types: &HashSet<TypeIdentity>,
828 container_name: Option<&str>,
829) -> Result<(), Error> {
830 if let Some(resolved) = resolve_phased_type(ty, mode, "type")? {
831 *ty = resolved;
832 }
833
834 if let Some(converted) = conversion_datatype_for_mode(ty, mode)?
835 && converted != *ty
836 {
837 *ty = converted;
838 return rewrite_datatype_for_phase(
839 ty,
840 mode,
841 original_types,
842 generated,
843 split_types,
844 container_name,
845 );
846 }
847
848 match ty {
849 DataType::Struct(s) => {
850 let container_default = SerdeContainerAttrs::from_attributes(&s.attributes)?
851 .is_some_and(|attrs| attrs.default);
852 let container_rename_all = container_rename_all_rule(
853 &s.attributes,
854 mode,
855 "struct rename_all",
856 container_name.unwrap_or("<anonymous struct>"),
857 )?;
858
859 rewrite_fields_for_phase(
860 &mut s.fields,
861 mode,
862 original_types,
863 generated,
864 split_types,
865 container_rename_all,
866 container_default,
867 false,
868 )?;
869 rewrite_struct_repr_for_phase(s, mode, container_name)?;
870 if let Some(intersection) = lower_field_aliases_for_phase(&mut s.fields, mode)? {
871 *ty = intersection;
872 return Ok(());
873 }
874 if let Some(intersection) = lower_flattened_struct(s)? {
875 *ty = intersection;
876 }
877 }
878 DataType::Enum(e) => {
879 filter_enum_variants_for_phase(e, mode)?;
880 let container_attrs = SerdeContainerAttrs::from_attributes(&e.attributes)?;
881
882 for (variant_name, variant) in &mut e.variants {
883 let rename_rule =
884 enum_variant_field_rename_rule(&container_attrs, variant, mode, variant_name)?;
885
886 rewrite_fields_for_phase(
887 &mut variant.fields,
888 mode,
889 original_types,
890 generated,
891 split_types,
892 rename_rule,
893 false,
894 true,
895 )?;
896
897 if let Some(aliases) = lower_field_aliases_for_phase(&mut variant.fields, mode)? {
898 variant.fields = Variant::unnamed().field(Field::new(aliases)).build().fields;
899 }
900 }
901
902 if rewrite_identifier_enum_for_phase(e, mode, original_types, generated, split_types)? {
903 return Ok(());
904 }
905
906 rewrite_enum_repr_for_phase(e, mode, original_types)?;
907 }
908 DataType::Tuple(tuple) => {
909 for ty in &mut tuple.elements {
910 rewrite_datatype_for_phase(ty, mode, original_types, generated, split_types, None)?;
911 }
912 }
913 DataType::List(list) => rewrite_datatype_for_phase(
914 &mut list.ty,
915 mode,
916 original_types,
917 generated,
918 split_types,
919 None,
920 )?,
921 DataType::Map(map) => {
922 rewrite_datatype_for_phase(
923 map.key_ty_mut(),
924 mode,
925 original_types,
926 generated,
927 split_types,
928 None,
929 )?;
930 rewrite_datatype_for_phase(
931 map.value_ty_mut(),
932 mode,
933 original_types,
934 generated,
935 split_types,
936 None,
937 )?;
938 }
939 DataType::Intersection(types_) => {
940 for ty in types_ {
941 rewrite_datatype_for_phase(ty, mode, original_types, generated, split_types, None)?;
942 }
943 }
944 DataType::Nullable(inner) => {
945 rewrite_datatype_for_phase(inner, mode, original_types, generated, split_types, None)?
946 }
947 DataType::Reference(Reference::Named(reference)) => {
948 if let NamedReferenceType::Inline { dt, .. } = &mut reference.inner {
949 rewrite_datatype_for_phase(dt, mode, original_types, generated, split_types, None)?;
950 }
951
952 let Some(referenced_ndt) = original_types.get(reference) else {
953 return Ok(());
954 };
955 let key = TypeIdentity::from_ndt(referenced_ndt);
956 for (_, dt) in named_reference_generics_mut(reference) {
957 rewrite_datatype_for_phase(dt, mode, original_types, generated, split_types, None)?;
958 }
959
960 if !split_types.contains(&key) {
961 return Ok(());
962 }
963
964 let Some(target) = generated.get(&key) else {
965 return Ok(());
966 };
967
968 let Reference::Named(reference_from_target) = (match mode {
969 PhaseRewrite::Unified => {
970 unreachable!("unified mode should not reference split types")
971 }
972 PhaseRewrite::Serialize => target
973 .serialize
974 .reference(named_reference_generics(reference).to_vec()),
975 PhaseRewrite::Deserialize => target
976 .deserialize
977 .reference(named_reference_generics(reference).to_vec()),
978 }) else {
979 unreachable!("named types always produce named references")
980 };
981 *reference = reference_from_target;
982 }
983 DataType::Reference(Reference::Opaque(_))
984 | DataType::Generic(_)
985 | DataType::Primitive(_) => {}
986 }
987
988 Ok(())
989}
990
991fn lower_flattened_struct(strct: &mut Struct) -> Result<Option<DataType>, Error> {
992 let Fields::Named(named) = &mut strct.fields else {
993 return Ok(None);
994 };
995
996 let has_flattened = named
997 .fields
998 .iter()
999 .any(|(_, field)| field_is_flattened(field));
1000 if !has_flattened {
1001 return Ok(None);
1002 }
1003
1004 let fields = std::mem::take(&mut named.fields);
1005 let mut base = Struct::named();
1006 let mut parts = Vec::new();
1007
1008 for (name, field) in fields {
1009 if field_is_flattened(&field) {
1010 if let Some(ty) = field.ty {
1011 parts.push(ty);
1012 }
1013 } else {
1014 base.field_mut(name, field);
1015 }
1016 }
1017
1018 let mut base = match base.build() {
1019 DataType::Struct(base) => base,
1020 _ => unreachable!("Struct::named always builds a struct"),
1021 };
1022 if matches!(&base.fields, Fields::Named(named) if !named.fields.is_empty()) {
1023 base.attributes = strct.attributes.clone();
1024 parts.insert(0, DataType::Struct(base));
1025 }
1026
1027 Ok(Some(DataType::Intersection(parts)))
1028}
1029
1030fn lower_field_aliases_for_phase(
1031 fields: &mut Fields,
1032 mode: PhaseRewrite,
1033) -> Result<Option<DataType>, Error> {
1034 if mode != PhaseRewrite::Deserialize {
1035 return Ok(None);
1036 }
1037
1038 let Fields::Named(named) = fields else {
1039 return Ok(None);
1040 };
1041
1042 if !named
1043 .fields
1044 .iter()
1045 .any(|(_, field)| field_has_aliases(field))
1046 {
1047 return Ok(None);
1048 }
1049
1050 let mut base = Struct::named();
1051 let mut parts = Vec::new();
1052
1053 for (name, field) in std::mem::take(&mut named.fields) {
1054 let Some(attrs) = SerdeFieldAttrs::from_attributes(&field.attributes)? else {
1055 base.field_mut(name, field);
1056 continue;
1057 };
1058
1059 if attrs.aliases.is_empty() {
1060 base.field_mut(name, field);
1061 continue;
1062 }
1063
1064 let mut accepted_names = Vec::with_capacity(attrs.aliases.len() + 1);
1065 accepted_names.push(name);
1066 accepted_names.extend(attrs.aliases.into_iter().map(Cow::Owned));
1067 parts.push(alias_field_union(accepted_names, field));
1068 }
1069
1070 let base = match base.build() {
1071 DataType::Struct(base) => base,
1072 _ => unreachable!("Struct::named always builds a struct"),
1073 };
1074
1075 if matches!(&base.fields, Fields::Named(named) if !named.fields.is_empty()) {
1076 parts.insert(0, DataType::Struct(base));
1077 }
1078
1079 Ok(Some(DataType::Intersection(parts)))
1080}
1081
1082fn field_has_aliases(field: &Field) -> bool {
1083 SerdeFieldAttrs::from_attributes(&field.attributes)
1084 .ok()
1085 .flatten()
1086 .is_some_and(|attrs| !attrs.aliases.is_empty())
1087}
1088
1089fn alias_field_union(names: Vec<Cow<'static, str>>, field: Field) -> DataType {
1090 let mut aliases = Enum::default();
1091 let empty_variant = Variant::unnamed().build();
1092
1093 for name in names {
1094 let mut field = field.clone();
1095 field.attributes.remove(parser::FIELD_ALIASES);
1096
1097 aliases.variants.push((
1098 Cow::Borrowed(""),
1099 clone_variant_with_unnamed_fields(
1100 &empty_variant,
1101 vec![Field::new(named_fields_datatype(vec![(name, field)]))],
1102 ),
1103 ));
1104 }
1105
1106 DataType::Enum(aliases)
1107}
1108
1109fn field_is_flattened(field: &Field) -> bool {
1110 SerdeFieldAttrs::from_attributes(&field.attributes)
1111 .ok()
1112 .flatten()
1113 .is_some_and(|attrs| attrs.flatten)
1114}
1115
1116fn rewrite_fields_for_phase(
1117 fields: &mut Fields,
1118 mode: PhaseRewrite,
1119 original_types: &Types,
1120 generated: &HashMap<TypeIdentity, SplitGeneratedTypes>,
1121 split_types: &HashSet<TypeIdentity>,
1122 rename_all_rule: Option<RenameRule>,
1123 container_default: bool,
1124 preserve_skipped_unnamed_fields: bool,
1125) -> Result<(), Error> {
1126 match fields {
1127 Fields::Unit => {}
1128 Fields::Unnamed(unnamed) => {
1129 for field in &mut unnamed.fields {
1130 if should_skip_field_for_mode(field, mode)? {
1131 if preserve_skipped_unnamed_fields {
1132 *field = skipped_field_marker(field);
1133 }
1134
1135 continue;
1136 }
1137
1138 apply_field_attrs(field, mode, container_default)?;
1139 rewrite_field_for_phase(field, mode, original_types, generated, split_types)?;
1140 }
1141
1142 if !preserve_skipped_unnamed_fields {
1143 unnamed.fields.retain(|field| field.ty.as_ref().is_some());
1144 }
1145 }
1146 Fields::Named(named) => {
1147 let mut skip_err = None;
1148 named
1149 .fields
1150 .retain(|(_, field)| match should_skip_field_for_mode(field, mode) {
1151 Ok(skip) => !skip,
1152 Err(err) => {
1153 skip_err = Some(err);
1154 true
1155 }
1156 });
1157 if let Some(err) = skip_err {
1158 return Err(err);
1159 }
1160
1161 for (name, field) in &mut named.fields {
1162 apply_field_attrs(field, mode, container_default)?;
1163
1164 if let Some(serde_attrs) = SerdeFieldAttrs::from_attributes(&field.attributes)? {
1165 let rename = select_phase_string(
1166 mode,
1167 serde_attrs.rename_serialize.as_deref(),
1168 serde_attrs.rename_deserialize.as_deref(),
1169 "field rename",
1170 name.as_ref(),
1171 )?;
1172
1173 if let Some(rename) = rename {
1174 *name = Cow::Owned(rename.to_string());
1175 } else if let Some(rule) = rename_all_rule {
1176 *name = Cow::Owned(rule.apply_to_field(name.as_ref()));
1177 }
1178 } else if let Some(rule) = rename_all_rule {
1179 *name = Cow::Owned(rule.apply_to_field(name.as_ref()));
1180 }
1181
1182 rewrite_field_for_phase(field, mode, original_types, generated, split_types)?;
1183 }
1184 }
1185 }
1186
1187 Ok(())
1188}
1189
1190fn rewrite_field_for_phase(
1191 field: &mut Field,
1192 mode: PhaseRewrite,
1193 original_types: &Types,
1194 generated: &HashMap<TypeIdentity, SplitGeneratedTypes>,
1195 split_types: &HashSet<TypeIdentity>,
1196) -> Result<(), Error> {
1197 if let Some(attrs) = SerdeFieldAttrs::from_attributes(&field.attributes)?
1198 && attrs.skip_serializing_if.is_some()
1199 {
1200 if let PhaseRewrite::Serialize = mode {
1201 field.optional = true;
1202 }
1203 field.attributes.remove(parser::FIELD_SKIP_SERIALIZING_IF);
1211 }
1212
1213 if let Some(ty) = field.ty.clone()
1214 && let Some(resolved) = resolve_phased_type(&ty, mode, "field")?
1215 {
1216 field.ty = Some(resolved);
1217 }
1218
1219 if let Some(ty) = field.ty.as_mut() {
1220 rewrite_datatype_for_phase(ty, mode, original_types, generated, split_types, None)?;
1221 }
1222
1223 Ok(())
1224}
1225
1226fn rewrite_struct_repr_for_phase(
1227 strct: &mut Struct,
1228 mode: PhaseRewrite,
1229 container_name: Option<&str>,
1230) -> Result<(), Error> {
1231 let Some((tag, rename_serialize, rename_deserialize)) =
1232 SerdeContainerAttrs::from_attributes(&strct.attributes)?.map(|attrs| {
1233 (
1234 attrs.tag.clone(),
1235 attrs.rename_serialize.clone(),
1236 attrs.rename_deserialize.clone(),
1237 )
1238 })
1239 else {
1240 return Ok(());
1241 };
1242
1243 let Some(tag) = tag.as_deref() else {
1244 return Ok(());
1245 };
1246
1247 let Fields::Named(named) = &mut strct.fields else {
1248 return Ok(());
1249 };
1250
1251 if named.fields.iter().any(|(name, field)| {
1252 name.as_ref() == tag
1253 && field
1254 .ty
1255 .as_ref()
1256 .is_some_and(is_generated_string_literal_datatype)
1257 }) {
1258 return Ok(());
1259 }
1260
1261 let serialized_name = match select_phase_string(
1262 mode,
1263 rename_serialize.as_deref(),
1264 rename_deserialize.as_deref(),
1265 "struct rename",
1266 container_name.unwrap_or("<anonymous struct>"),
1267 )? {
1268 Some(rename) => rename.to_string(),
1269 None => container_name
1270 .map(str::to_owned)
1271 .ok_or_else(|| {
1272 Error::invalid_phased_type_usage(
1273 "<anonymous struct>",
1274 "`#[serde(tag = ...)]` on structs requires either a named type or `#[serde(rename = ...)]`",
1275 )
1276 })?,
1277 };
1278
1279 named.fields.insert(
1280 0,
1281 (
1282 Cow::Owned(tag.to_string()),
1283 Field::new(string_literal_datatype(serialized_name)),
1284 ),
1285 );
1286
1287 Ok(())
1288}
1289
1290fn should_skip_field_for_mode(field: &Field, mode: PhaseRewrite) -> Result<bool, Error> {
1291 let Some(attrs) = SerdeFieldAttrs::from_attributes(&field.attributes)? else {
1292 return Ok(false);
1293 };
1294
1295 Ok(match mode {
1296 PhaseRewrite::Serialize => attrs.skip_serializing,
1297 PhaseRewrite::Deserialize => attrs.skip_deserializing,
1298 PhaseRewrite::Unified => attrs.skip_serializing || attrs.skip_deserializing,
1299 })
1300}
1301
1302fn skipped_field_marker(field: &Field) -> Field {
1303 let mut skipped = Field::default();
1304 skipped.optional = field.optional;
1305 skipped.deprecated = field.deprecated.clone();
1306 skipped.docs = field.docs.clone();
1307 skipped.attributes = field.attributes.clone();
1308 skipped
1309}
1310
1311fn unnamed_live_fields(unnamed: &UnnamedFields) -> impl Iterator<Item = &Field> {
1312 unnamed.fields.iter().filter(|field| field.ty.is_some())
1313}
1314
1315fn unnamed_live_field_count(unnamed: &UnnamedFields) -> usize {
1316 unnamed_live_fields(unnamed).count()
1317}
1318
1319fn unnamed_has_effective_payload(unnamed: &UnnamedFields) -> bool {
1320 unnamed_live_field_count(unnamed) != 0
1321}
1322
1323fn unnamed_fields_all_skipped(unnamed: &UnnamedFields) -> bool {
1324 !unnamed.fields.is_empty() && !unnamed_has_effective_payload(unnamed)
1325}
1326
1327fn rewrite_enum_repr_for_phase(
1328 e: &mut Enum,
1329 mode: PhaseRewrite,
1330 original_types: &Types,
1331) -> Result<(), Error> {
1332 if enum_repr_already_rewritten(e) {
1333 return Ok(());
1334 }
1335
1336 let repr = EnumRepr::from_attrs(&e.attributes)?;
1337 if matches!(repr, EnumRepr::Untagged) {
1338 return Ok(());
1339 }
1340
1341 let container_attrs = SerdeContainerAttrs::from_attributes(&e.attributes)?;
1342 let variants = std::mem::take(&mut e.variants);
1343 let mut transformed = Vec::with_capacity(variants.len());
1344 for (variant_name, variant) in variants {
1345 if variant.skip {
1346 continue;
1347 }
1348
1349 let variant_attrs = SerdeVariantAttrs::from_attributes(&variant.attributes)?;
1350 if variant_attrs
1351 .as_ref()
1352 .is_some_and(|attrs| variant_is_skipped_for_mode(attrs, mode))
1353 {
1354 continue;
1355 }
1356
1357 if variant_attrs.as_ref().is_some_and(|attrs| attrs.untagged) {
1358 transformed.push((
1359 Cow::Owned(variant_name.into_owned()),
1360 transform_untagged_variant(&variant)?,
1361 ));
1362 continue;
1363 }
1364
1365 let serialized_name =
1366 serialized_variant_name(&variant_name, &variant, &container_attrs, mode)?;
1367 let aliases = variant_attrs
1368 .as_ref()
1369 .filter(|_| mode == PhaseRewrite::Deserialize)
1370 .map(|attrs| attrs.aliases.as_slice())
1371 .unwrap_or(&[]);
1372 let names = std::iter::once(serialized_name).chain(aliases.iter().cloned());
1373
1374 for serialized_name in names {
1375 let widen_tag = mode == PhaseRewrite::Deserialize
1376 && variant_attrs.as_ref().is_some_and(|attrs| attrs.other);
1377 let mut transformed_variant = match &repr {
1378 EnumRepr::External => {
1379 transform_external_variant(serialized_name.clone(), &variant)?
1380 }
1381 EnumRepr::Internal { tag } => transform_internal_variant(
1382 serialized_name.clone(),
1383 tag.as_ref(),
1384 &variant,
1385 original_types,
1386 widen_tag,
1387 )?,
1388 EnumRepr::Adjacent { tag, content } => {
1389 if tag == content {
1390 return Err(Error::invalid_enum_representation(
1391 "serde adjacent tagging requires distinct `tag` and `content` field names",
1392 ));
1393 }
1394
1395 transform_adjacent_variant(
1396 serialized_name.clone(),
1397 tag.as_ref(),
1398 content.as_ref(),
1399 &variant,
1400 widen_tag,
1401 )?
1402 }
1403 EnumRepr::Untagged => unreachable!(),
1404 };
1405
1406 transformed_variant.attributes = Default::default();
1407 transformed.push((Cow::Owned(serialized_name), transformed_variant));
1408 }
1409 }
1410
1411 e.variants = transformed;
1412 e.attributes = Default::default();
1413
1414 Ok(())
1415}
1416
1417fn enum_repr_already_rewritten(e: &Enum) -> bool {
1418 e.attributes.is_empty()
1419 && !e.variants.is_empty()
1420 && e.variants.iter().all(|(name, variant)| {
1421 variant.attributes.is_empty() && variant_repr_already_rewritten(name, variant)
1422 })
1423}
1424
1425fn variant_repr_already_rewritten(name: &str, variant: &Variant) -> bool {
1426 match &variant.fields {
1427 Fields::Unit => false,
1428 Fields::Unnamed(fields) if name.is_empty() => unnamed_live_field_count(fields) == 1,
1429 Fields::Unnamed(fields) if fields.fields.len() == 1 => fields
1430 .fields
1431 .first()
1432 .and_then(|field| field.ty.as_ref())
1433 .is_some_and(is_generated_string_literal_datatype),
1434 Fields::Named(fields) => fields.fields.iter().any(|(field_name, field)| {
1435 field_name == name
1436 || field
1437 .ty
1438 .as_ref()
1439 .is_some_and(is_generated_string_literal_datatype)
1440 }),
1441 _ => false,
1442 }
1443}
1444
1445fn rewrite_identifier_enum_for_phase(
1446 e: &mut Enum,
1447 mode: PhaseRewrite,
1448 original_types: &Types,
1449 generated: &HashMap<TypeIdentity, SplitGeneratedTypes>,
1450 split_types: &HashSet<TypeIdentity>,
1451) -> Result<bool, Error> {
1452 let Some(attrs) = SerdeContainerAttrs::from_attributes(&e.attributes)? else {
1453 return Ok(false);
1454 };
1455
1456 if !attrs.variant_identifier && !attrs.field_identifier {
1457 return Ok(false);
1458 }
1459
1460 if mode != PhaseRewrite::Deserialize {
1461 return Ok(false);
1462 }
1463
1464 let container_attrs = SerdeContainerAttrs::from_attributes(&e.attributes)?;
1465 let mut variants = Vec::new();
1466 let mut seen = HashSet::new();
1467
1468 for (variant_name, variant) in e.variants.iter() {
1469 let serialized_name = serialized_variant_name(
1470 variant_name,
1471 variant,
1472 &container_attrs,
1473 PhaseRewrite::Deserialize,
1474 )?;
1475
1476 if seen.insert(serialized_name.clone()) {
1477 variants.push((
1478 Cow::Owned(serialized_name.clone()),
1479 identifier_union_variant(string_literal_datatype(serialized_name)),
1480 ));
1481 }
1482
1483 if let Some(variant_attrs) = SerdeVariantAttrs::from_attributes(&variant.attributes)? {
1484 for alias in &variant_attrs.aliases {
1485 if seen.insert(alias.clone()) {
1486 variants.push((
1487 Cow::Owned(alias.clone()),
1488 identifier_union_variant(string_literal_datatype(alias.clone())),
1489 ));
1490 }
1491 }
1492 }
1493 }
1494
1495 variants.push((
1496 Cow::Borrowed(""),
1497 identifier_union_variant(DataType::Primitive(specta::datatype::Primitive::u32)),
1498 ));
1499
1500 if attrs.field_identifier
1501 && let Some((_, fallback)) = &e.variants.last()
1502 && let Fields::Unnamed(unnamed) = &fallback.fields
1503 && let Some(field) = unnamed.fields.first()
1504 && let Some(ty) = field.ty.as_ref()
1505 {
1506 let mut fallback_ty = ty.clone();
1507 rewrite_datatype_for_phase(
1508 &mut fallback_ty,
1509 mode,
1510 original_types,
1511 generated,
1512 split_types,
1513 None,
1514 )?;
1515 variants.push((Cow::Borrowed(""), identifier_union_variant(fallback_ty)));
1516 }
1517
1518 e.attributes = Default::default();
1519 e.variants = variants;
1520 Ok(true)
1521}
1522
1523fn container_rename_all_rule(
1524 attrs: &specta::datatype::Attributes,
1525 mode: PhaseRewrite,
1526 context: &str,
1527 container_name: &str,
1528) -> Result<Option<RenameRule>, Error> {
1529 let attrs = SerdeContainerAttrs::from_attributes(attrs)?;
1530
1531 select_phase_rule(
1532 mode,
1533 attrs.as_ref().and_then(|attrs| attrs.rename_all_serialize),
1534 attrs
1535 .as_ref()
1536 .and_then(|attrs| attrs.rename_all_deserialize),
1537 context,
1538 container_name,
1539 )
1540}
1541
1542fn enum_variant_field_rename_rule(
1543 container_attrs: &Option<SerdeContainerAttrs>,
1544 variant: &Variant,
1545 mode: PhaseRewrite,
1546 variant_name: &str,
1547) -> Result<Option<RenameRule>, Error> {
1548 let variant_attrs = SerdeVariantAttrs::from_attributes(&variant.attributes)?;
1549
1550 let variant_rule = select_phase_rule(
1551 mode,
1552 variant_attrs
1553 .as_ref()
1554 .and_then(|attrs| attrs.rename_all_serialize),
1555 variant_attrs
1556 .as_ref()
1557 .and_then(|attrs| attrs.rename_all_deserialize),
1558 "enum variant rename_all",
1559 variant_name,
1560 )?;
1561
1562 if variant_rule.is_some() {
1563 return Ok(variant_rule);
1564 }
1565
1566 select_phase_rule(
1567 mode,
1568 container_attrs
1569 .as_ref()
1570 .and_then(|attrs| attrs.rename_all_fields_serialize),
1571 container_attrs
1572 .as_ref()
1573 .and_then(|attrs| attrs.rename_all_fields_deserialize),
1574 "enum rename_all_fields",
1575 variant_name,
1576 )
1577}
1578
1579fn identifier_union_variant(ty: DataType) -> Variant {
1580 let mut variant = Variant::unnamed().build();
1581 if let Fields::Unnamed(fields) = &mut variant.fields {
1582 fields.fields.push(Field::new(ty));
1583 }
1584 variant
1585}
1586
1587fn transform_untagged_variant(variant: &Variant) -> Result<Variant, Error> {
1588 let payload = variant_payload_field(variant)
1589 .ok_or_else(|| Error::invalid_external_tagged_variant("<untagged variant>"))?;
1590 Ok(clone_variant_with_unnamed_fields(variant, vec![payload]))
1591}
1592
1593fn filter_enum_variants_for_phase(e: &mut Enum, mode: PhaseRewrite) -> Result<(), Error> {
1594 let mut filter_err = None;
1595 e.variants.retain(|(_, variant)| {
1596 if variant.skip {
1597 return false;
1598 }
1599
1600 match SerdeVariantAttrs::from_attributes(&variant.attributes) {
1601 Ok(Some(attrs)) => !variant_is_skipped_for_mode(&attrs, mode),
1602 Ok(None) => true,
1603 Err(err) => {
1604 filter_err = Some(err);
1605 true
1606 }
1607 }
1608 });
1609
1610 if let Some(err) = filter_err {
1611 return Err(err);
1612 }
1613
1614 Ok(())
1615}
1616
1617fn variant_is_skipped_for_mode(attrs: &SerdeVariantAttrs, mode: PhaseRewrite) -> bool {
1618 match mode {
1619 PhaseRewrite::Serialize => attrs.skip_serializing,
1620 PhaseRewrite::Deserialize => attrs.skip_deserializing,
1621 PhaseRewrite::Unified => attrs.skip_serializing || attrs.skip_deserializing,
1622 }
1623}
1624
1625fn serialized_variant_name(
1626 variant_name: &str,
1627 variant: &Variant,
1628 container_attrs: &Option<SerdeContainerAttrs>,
1629 mode: PhaseRewrite,
1630) -> Result<String, Error> {
1631 let variant_attrs = SerdeVariantAttrs::from_attributes(&variant.attributes)?;
1632
1633 if let Some(rename) = select_phase_string(
1634 mode,
1635 variant_attrs
1636 .as_ref()
1637 .and_then(|attrs| attrs.rename_serialize.as_deref()),
1638 variant_attrs
1639 .as_ref()
1640 .and_then(|attrs| attrs.rename_deserialize.as_deref()),
1641 "enum variant rename",
1642 variant_name,
1643 )? {
1644 return Ok(rename.to_string());
1645 }
1646
1647 Ok(select_phase_rule(
1648 mode,
1649 container_attrs
1650 .as_ref()
1651 .and_then(|attrs| attrs.rename_all_serialize),
1652 container_attrs
1653 .as_ref()
1654 .and_then(|attrs| attrs.rename_all_deserialize),
1655 "enum rename_all",
1656 variant_name,
1657 )?
1658 .map_or_else(
1659 || variant_name.to_string(),
1660 |rule| rule.apply_to_variant(variant_name),
1661 ))
1662}
1663
1664fn select_phase_string<'a>(
1665 mode: PhaseRewrite,
1666 serialize: Option<&'a str>,
1667 deserialize: Option<&'a str>,
1668 context: &str,
1669 name: &str,
1670) -> Result<Option<&'a str>, Error> {
1671 Ok(match mode {
1672 PhaseRewrite::Serialize => serialize,
1673 PhaseRewrite::Deserialize => deserialize,
1674 PhaseRewrite::Unified => match (serialize, deserialize) {
1675 (Some(serialize), Some(deserialize)) if serialize != deserialize => {
1676 return Err(Error::incompatible_rename(
1677 context.to_string(),
1678 name,
1679 Some(serialize.to_string()),
1680 Some(deserialize.to_string()),
1681 ));
1682 }
1683 (serialize, deserialize) => serialize.or(deserialize),
1684 },
1685 })
1686}
1687
1688fn select_phase_rule(
1689 mode: PhaseRewrite,
1690 serialize: Option<RenameRule>,
1691 deserialize: Option<RenameRule>,
1692 context: &str,
1693 name: &str,
1694) -> Result<Option<RenameRule>, Error> {
1695 Ok(match mode {
1696 PhaseRewrite::Serialize => serialize,
1697 PhaseRewrite::Deserialize => deserialize,
1698 PhaseRewrite::Unified => match (serialize, deserialize) {
1699 (Some(serialize), Some(deserialize)) if serialize != deserialize => {
1700 return Err(Error::incompatible_rename(
1701 context.to_string(),
1702 name,
1703 Some(format!("{serialize:?}")),
1704 Some(format!("{deserialize:?}")),
1705 ));
1706 }
1707 (serialize, deserialize) => serialize.or(deserialize),
1708 },
1709 })
1710}
1711
1712fn resolve_phased_type(
1713 ty: &DataType,
1714 mode: PhaseRewrite,
1715 path: &str,
1716) -> Result<Option<DataType>, Error> {
1717 let DataType::Reference(Reference::Opaque(reference)) = ty else {
1718 return Ok(None);
1719 };
1720 let Some(phased) = reference.downcast_ref::<PhasedTy>() else {
1721 return Ok(None);
1722 };
1723
1724 Ok(match mode {
1725 PhaseRewrite::Unified => {
1727 return Err(Error::invalid_phased_type_usage(
1728 path,
1729 "`specta_serde::Phased<Serialize, Deserialize>` requires `PhasesFormat`",
1730 ));
1731 }
1732 PhaseRewrite::Serialize => Some(phased.serialize.clone()),
1733 PhaseRewrite::Deserialize => Some(phased.deserialize.clone()),
1734 })
1735}
1736
1737fn conversion_datatype_for_mode(
1738 ty: &DataType,
1739 mode: PhaseRewrite,
1740) -> Result<Option<DataType>, Error> {
1741 let attrs = match ty {
1742 DataType::Struct(s) => &s.attributes,
1743 DataType::Enum(e) => &e.attributes,
1744 _ => return Ok(None),
1745 };
1746
1747 select_conversion_target(attrs, mode)
1748}
1749
1750fn select_conversion_target(
1751 attrs: &specta::datatype::Attributes,
1752 mode: PhaseRewrite,
1753) -> Result<Option<DataType>, Error> {
1754 let parsed = SerdeContainerAttrs::from_attributes(attrs)?;
1755 let resolved = parsed.as_ref();
1756
1757 let serialize_target = resolved.and_then(|v| v.resolved_into.as_ref());
1758 let deserialize_target =
1759 resolved.and_then(|v| v.resolved_from.as_ref().or(v.resolved_try_from.as_ref()));
1760
1761 match mode {
1762 PhaseRewrite::Serialize => Ok(serialize_target.cloned()),
1763 PhaseRewrite::Deserialize => Ok(deserialize_target.cloned()),
1764 PhaseRewrite::Unified => match (serialize_target, deserialize_target) {
1765 (None, None) => Ok(None),
1766 (Some(serialize), Some(deserialize)) if serialize == deserialize => {
1767 Ok(Some(serialize.clone()))
1768 }
1769 _ => Err(Error::incompatible_conversion(
1770 "container conversion",
1771 resolved
1772 .and_then(|attrs| {
1773 attrs
1774 .into
1775 .as_ref()
1776 .map(|v| format!("into({})", v.type_src))
1777 .or_else(|| {
1778 attrs.from.as_ref().map(|v| format!("from({})", v.type_src))
1779 })
1780 .or_else(|| {
1781 attrs
1782 .try_from
1783 .as_ref()
1784 .map(|v| format!("try_from({})", v.type_src))
1785 })
1786 })
1787 .unwrap_or_else(|| "<container>".to_string()),
1788 resolved.and_then(|attrs| attrs.into.as_ref().map(|v| v.type_src.clone())),
1789 resolved.and_then(|attrs| {
1790 attrs.from.as_ref().map(|v| v.type_src.clone()).or_else(|| {
1791 attrs
1792 .try_from
1793 .as_ref()
1794 .map(|v| format!("try_from({})", v.type_src))
1795 })
1796 }),
1797 )),
1798 },
1799 }
1800}
1801
1802fn transform_external_variant(
1803 serialized_name: String,
1804 variant: &Variant,
1805) -> Result<Variant, Error> {
1806 let skipped_only_unnamed = match &variant.fields {
1807 Fields::Unnamed(unnamed) => unnamed_fields_all_skipped(unnamed),
1808 Fields::Unit | Fields::Named(_) => false,
1809 };
1810
1811 Ok(match &variant.fields {
1812 Fields::Unit => clone_variant_with_unnamed_fields(
1813 variant,
1814 vec![Field::new(string_literal_datatype(serialized_name))],
1815 ),
1816 _ if skipped_only_unnamed => clone_variant_with_unnamed_fields(
1817 variant,
1818 vec![Field::new(string_literal_datatype(serialized_name))],
1819 ),
1820 _ => {
1821 let payload = variant_payload_field(variant)
1822 .ok_or_else(|| Error::invalid_external_tagged_variant(serialized_name.clone()))?;
1823
1824 clone_variant_with_named_fields(variant, vec![(Cow::Owned(serialized_name), payload)])
1825 }
1826 })
1827}
1828
1829fn transform_adjacent_variant(
1830 serialized_name: String,
1831 tag: &str,
1832 content: &str,
1833 variant: &Variant,
1834 widen_tag: bool,
1835) -> Result<Variant, Error> {
1836 let mut fields = vec![(
1837 Cow::Owned(tag.to_string()),
1838 Field::new(if widen_tag {
1839 DataType::Primitive(Primitive::str)
1840 } else {
1841 string_literal_datatype(serialized_name.clone())
1842 }),
1843 )];
1844
1845 if variant_has_effective_payload(variant) {
1846 let payload = variant_payload_field(variant)
1847 .ok_or_else(|| Error::invalid_adjacent_tagged_variant(serialized_name.clone()))?;
1848 fields.push((Cow::Owned(content.to_string()), payload));
1849 }
1850
1851 Ok(clone_variant_with_named_fields(variant, fields))
1852}
1853
1854fn transform_internal_variant(
1855 serialized_name: String,
1856 tag: &str,
1857 variant: &Variant,
1858 original_types: &Types,
1859 widen_tag: bool,
1860) -> Result<Variant, Error> {
1861 let mut fields = vec![(
1862 Cow::Owned(tag.to_string()),
1863 Field::new(if widen_tag {
1864 DataType::Primitive(Primitive::str)
1865 } else {
1866 string_literal_datatype(serialized_name.clone())
1867 }),
1868 )];
1869
1870 match &variant.fields {
1871 Fields::Unit => {}
1872 Fields::Named(named) => {
1873 fields.extend(named.fields.iter().cloned());
1874 }
1875 Fields::Unnamed(unnamed) => {
1876 let live_field_count = unnamed_live_field_count(unnamed);
1877
1878 if live_field_count == 0 {
1879 return Ok(clone_variant_with_named_fields(variant, fields));
1880 }
1881
1882 let non_skipped = unnamed_live_fields(unnamed).collect::<Vec<_>>();
1883
1884 if live_field_count != 1 {
1885 return Err(Error::invalid_internally_tagged_variant(
1886 serialized_name,
1887 "tuple variant must have exactly one non-skipped field",
1888 ));
1889 }
1890
1891 let payload_field = non_skipped
1892 .into_iter()
1893 .next()
1894 .expect("checked above")
1895 .clone();
1896 let payload_ty = payload_field.ty.clone().expect("checked above");
1897 let Some(payload_is_effectively_empty) = internal_tag_payload_compatibility(
1898 &payload_ty,
1899 original_types,
1900 &mut HashSet::new(),
1901 )?
1902 else {
1903 return Err(Error::invalid_internally_tagged_variant(
1904 serialized_name,
1905 "payload cannot be merged with a tag",
1906 ));
1907 };
1908
1909 if !payload_is_effectively_empty {
1910 return Ok(clone_variant_with_unnamed_fields(
1911 variant,
1912 vec![Field::new(DataType::Intersection(vec![
1913 named_fields_datatype(fields),
1914 payload_ty,
1915 ]))],
1916 ));
1917 }
1918 }
1919 }
1920
1921 Ok(clone_variant_with_named_fields(variant, fields))
1922}
1923
1924fn named_fields_datatype(fields: Vec<(Cow<'static, str>, Field)>) -> DataType {
1925 let mut builder = Struct::named();
1926 for (name, field) in fields {
1927 builder = builder.field(name, field);
1928 }
1929
1930 builder.build()
1931}
1932
1933fn string_literal_datatype(value: String) -> DataType {
1934 let mut value_enum = Enum::default();
1935 value_enum
1936 .variants
1937 .push((Cow::Owned(value), Variant::unit()));
1938 DataType::Enum(value_enum)
1939}
1940
1941fn is_generated_string_literal_datatype(ty: &DataType) -> bool {
1942 let DataType::Enum(e) = ty else {
1943 return false;
1944 };
1945
1946 let Some((_, variant)) = e.variants.first() else {
1947 return false;
1948 };
1949
1950 if e.variants.len() != 1 {
1951 return false;
1952 }
1953
1954 match &variant.fields {
1955 Fields::Unit => true,
1956 Fields::Unnamed(fields) if fields.fields.len() == 1 => fields
1957 .fields
1958 .first()
1959 .and_then(|field| field.ty.as_ref())
1960 .is_some_and(is_generated_string_literal_datatype),
1961 _ => false,
1962 }
1963}
1964
1965fn variant_has_effective_payload(variant: &Variant) -> bool {
1966 match &variant.fields {
1967 Fields::Unit => false,
1968 Fields::Named(named) => !&named.fields.is_empty(),
1969 Fields::Unnamed(unnamed) => unnamed_has_effective_payload(unnamed),
1970 }
1971}
1972
1973fn variant_payload_field(variant: &Variant) -> Option<Field> {
1974 match &variant.fields {
1975 Fields::Unit => Some(Field::new(DataType::Tuple(Tuple::new(vec![])))),
1976 Fields::Named(named) => {
1977 let mut out = Struct::named();
1978 for (name, field) in named.fields.iter().cloned() {
1979 out.field_mut(name, field);
1980 }
1981 Some(Field::new(out.build()))
1982 }
1983 Fields::Unnamed(unnamed) => {
1984 let original_unnamed_len = unnamed.fields.len();
1985
1986 let non_skipped = unnamed_live_fields(unnamed).collect::<Vec<_>>();
1987
1988 match non_skipped.as_slice() {
1989 [] => Some(Field::new(DataType::Tuple(Tuple::new(vec![])))),
1990 [single] if original_unnamed_len == 1 => Some((*single).clone()),
1991 _ => Some(Field::new(DataType::Tuple(Tuple::new(
1992 non_skipped
1993 .iter()
1994 .filter_map(|field| field.ty.clone())
1995 .collect(),
1996 )))),
1997 }
1998 }
1999 }
2000}
2001
2002fn clone_variant_with_named_fields(
2003 original: &Variant,
2004 fields: Vec<(Cow<'static, str>, Field)>,
2005) -> Variant {
2006 let mut builder = Variant::named();
2007 for (name, field) in fields {
2008 builder = builder.field(name, field);
2009 }
2010
2011 let mut transformed = builder.build();
2012 transformed.skip = original.skip;
2013 transformed.docs = original.docs.clone();
2014 transformed.deprecated = original.deprecated.clone();
2015 transformed.attributes = original.attributes.clone();
2016 transformed
2017}
2018
2019fn clone_variant_with_unnamed_fields(original: &Variant, fields: Vec<Field>) -> Variant {
2020 let mut builder = Variant::unnamed();
2021 for field in fields {
2022 builder = builder.field(field);
2023 }
2024
2025 let mut transformed = builder.build();
2026 transformed.skip = original.skip;
2027 transformed.docs = original.docs.clone();
2028 transformed.deprecated = original.deprecated.clone();
2029 transformed.attributes = original.attributes.clone();
2030 transformed
2031}
2032
2033fn internal_tag_payload_compatibility(
2034 ty: &DataType,
2035 original_types: &Types,
2036 seen: &mut HashSet<TypeIdentity>,
2037) -> Result<Option<bool>, Error> {
2038 match ty {
2039 DataType::Map(_) => Ok(Some(false)),
2040 DataType::Struct(strct) => {
2041 if SerdeContainerAttrs::from_attributes(&strct.attributes)?
2042 .is_some_and(|attrs| attrs.transparent)
2043 {
2044 let payload_fields = match &strct.fields {
2045 Fields::Unit => return Ok(Some(true)),
2046 Fields::Unnamed(unnamed) => unnamed
2047 .fields
2048 .iter()
2049 .filter_map(|field| field.ty.as_ref())
2050 .collect::<Vec<_>>(),
2051 Fields::Named(named) => named
2052 .fields
2053 .iter()
2054 .filter_map(|(_, field)| field.ty.as_ref())
2055 .collect::<Vec<_>>(),
2056 };
2057
2058 let [inner_ty] = payload_fields.as_slice() else {
2059 if payload_fields.is_empty() {
2060 return Ok(Some(true));
2061 }
2062
2063 return Ok(None);
2064 };
2065
2066 return internal_tag_payload_compatibility(inner_ty, original_types, seen);
2067 }
2068
2069 Ok(match &strct.fields {
2070 Fields::Named(named) => Some(
2071 named
2072 .fields
2073 .iter()
2074 .all(|(_, field)| field.ty.as_ref().is_none()),
2075 ),
2076 Fields::Unit | Fields::Unnamed(_) => None,
2077 })
2078 }
2079 DataType::Tuple(tuple) => Ok(tuple.elements.is_empty().then_some(true)),
2080 DataType::Intersection(types) => {
2081 let mut is_effectively_empty = true;
2082
2083 for ty in types {
2084 let Some(part_empty) =
2085 internal_tag_payload_compatibility(ty, original_types, seen)?
2086 else {
2087 return Ok(None);
2088 };
2089
2090 is_effectively_empty &= part_empty;
2091 }
2092
2093 Ok(Some(is_effectively_empty))
2094 }
2095 DataType::Reference(Reference::Named(reference)) => {
2096 if let NamedReferenceType::Inline { dt, .. } = &reference.inner {
2097 return internal_tag_payload_compatibility(dt, original_types, seen);
2098 }
2099
2100 let Some(referenced) = original_types.get(reference) else {
2101 return Ok(None);
2102 };
2103 let Some(referenced_ty) = referenced.ty.as_ref() else {
2104 return Ok(None);
2105 };
2106
2107 let key = TypeIdentity::from_ndt(referenced);
2108 if !seen.insert(key.clone()) {
2109 return Ok(Some(false));
2110 }
2111
2112 let compatible =
2113 internal_tag_payload_compatibility(referenced_ty, original_types, seen);
2114 seen.remove(&key);
2115 compatible
2116 }
2117 DataType::Enum(enm) => match EnumRepr::from_attrs(&enm.attributes) {
2118 Ok(EnumRepr::Untagged) => {
2119 let mut is_effectively_empty = true;
2120 for (_, variant) in &enm.variants {
2121 let Some(variant_empty) =
2122 internal_tag_variant_payload_compatibility(variant, original_types, seen)?
2123 else {
2124 return Ok(None);
2125 };
2126
2127 is_effectively_empty &= variant_empty;
2128 }
2129
2130 Ok(Some(is_effectively_empty))
2131 }
2132 Ok(EnumRepr::External | EnumRepr::Internal { .. } | EnumRepr::Adjacent { .. }) => {
2133 Ok(Some(false))
2134 }
2135 Err(_) => Ok(None),
2136 },
2137 DataType::Primitive(_)
2138 | DataType::List(_)
2139 | DataType::Nullable(_)
2140 | DataType::Reference(Reference::Opaque(_))
2141 | DataType::Generic(_) => Ok(None),
2142 }
2143}
2144
2145fn internal_tag_variant_payload_compatibility(
2146 variant: &Variant,
2147 original_types: &Types,
2148 seen: &mut HashSet<TypeIdentity>,
2149) -> Result<Option<bool>, Error> {
2150 match &variant.fields {
2151 Fields::Unit => Ok(Some(true)),
2152 Fields::Named(named) => Ok(Some(
2153 named
2154 .fields
2155 .iter()
2156 .all(|(_, field)| field.ty.as_ref().is_none()),
2157 )),
2158 Fields::Unnamed(unnamed) => {
2159 if unnamed.fields.len() != 1 {
2160 return Ok(None);
2161 }
2162
2163 unnamed
2164 .fields
2165 .iter()
2166 .find_map(|field| field.ty.as_ref())
2167 .map_or(Ok(None), |ty| {
2168 internal_tag_payload_compatibility(ty, original_types, seen)
2169 })
2170 }
2171 }
2172}
2173
2174fn has_local_phase_difference(dt: &DataType) -> Result<bool, Error> {
2175 match dt {
2176 DataType::Struct(s) => Ok(container_has_local_difference(&s.attributes)?
2177 || fields_have_local_difference(&s.fields)?),
2178 DataType::Enum(e) => Ok(container_has_local_difference(&e.attributes)?
2179 || e.variants
2180 .iter()
2181 .try_fold(false, |has_difference, (_, variant)| {
2182 if has_difference {
2183 return Ok(true);
2184 }
2185
2186 Ok(variant_has_local_difference(variant)?
2187 || fields_have_local_difference(&variant.fields)?)
2188 })?),
2189 DataType::Tuple(tuple) => tuple.elements.iter().try_fold(false, |has_difference, ty| {
2190 if has_difference {
2191 return Ok(true);
2192 }
2193
2194 has_local_phase_difference(ty)
2195 }),
2196 DataType::List(list) => has_local_phase_difference(&list.ty),
2197 DataType::Map(map) => Ok(has_local_phase_difference(map.key_ty())?
2198 || has_local_phase_difference(map.value_ty())?),
2199 DataType::Intersection(types_) => types_.iter().try_fold(false, |has_difference, ty| {
2200 if has_difference {
2201 return Ok(true);
2202 }
2203
2204 has_local_phase_difference(ty)
2205 }),
2206 DataType::Nullable(inner) => has_local_phase_difference(inner),
2207 DataType::Reference(Reference::Opaque(reference)) => {
2208 Ok(reference.downcast_ref::<PhasedTy>().is_some())
2209 }
2210 DataType::Primitive(_)
2211 | DataType::Reference(Reference::Named(_))
2212 | DataType::Generic(_) => Ok(false),
2213 }
2214}
2215
2216fn container_has_local_difference(attrs: &specta::datatype::Attributes) -> Result<bool, Error> {
2217 let Some(conversions) = SerdeContainerAttrs::from_attributes(attrs)? else {
2218 return Ok(false);
2219 };
2220
2221 Ok(conversions.resolved_into.as_ref()
2222 != conversions
2223 .resolved_from
2224 .as_ref()
2225 .or(conversions.resolved_try_from.as_ref())
2226 || conversions.rename_serialize != conversions.rename_deserialize
2227 || conversions.rename_all_serialize != conversions.rename_all_deserialize
2228 || conversions.rename_all_fields_serialize != conversions.rename_all_fields_deserialize
2229 || conversions.variant_identifier
2230 || conversions.field_identifier)
2231}
2232
2233fn fields_have_local_difference(fields: &Fields) -> Result<bool, Error> {
2234 match fields {
2235 Fields::Unit => Ok(false),
2236 Fields::Unnamed(unnamed) => {
2237 unnamed
2238 .fields
2239 .iter()
2240 .try_fold(false, |has_difference, field| {
2241 if has_difference {
2242 return Ok(true);
2243 }
2244
2245 Ok(field_has_local_difference(field)?
2246 || field
2247 .ty
2248 .as_ref()
2249 .map_or(Ok(false), has_local_phase_difference)?)
2250 })
2251 }
2252 Fields::Named(named) => {
2253 named
2254 .fields
2255 .iter()
2256 .try_fold(false, |has_difference, (_, field)| {
2257 if has_difference {
2258 return Ok(true);
2259 }
2260
2261 Ok(field_has_local_difference(field)?
2262 || field
2263 .ty
2264 .as_ref()
2265 .map_or(Ok(false), has_local_phase_difference)?)
2266 })
2267 }
2268 }
2269}
2270
2271fn field_has_local_difference(field: &Field) -> Result<bool, Error> {
2272 Ok(SerdeFieldAttrs::from_attributes(&field.attributes)?
2273 .map(|attrs| {
2274 attrs.rename_serialize.as_deref() != attrs.rename_deserialize.as_deref()
2275 || !attrs.aliases.is_empty()
2276 || attrs.skip_serializing != attrs.skip_deserializing
2277 || attrs.skip_serializing_if.is_some()
2278 || attrs.has_serialize_with
2279 || attrs.has_deserialize_with
2280 || attrs.has_with
2281 })
2282 .unwrap_or_default())
2283}
2284
2285fn variant_has_local_difference(variant: &Variant) -> Result<bool, Error> {
2286 Ok(SerdeVariantAttrs::from_attributes(&variant.attributes)?
2287 .map(|attrs| {
2288 attrs.rename_serialize.as_deref() != attrs.rename_deserialize.as_deref()
2289 || attrs.rename_all_serialize != attrs.rename_all_deserialize
2290 || !attrs.aliases.is_empty()
2291 || attrs.skip_serializing != attrs.skip_deserializing
2292 || attrs.has_serialize_with
2293 || attrs.has_deserialize_with
2294 || attrs.has_with
2295 || attrs.other
2296 })
2297 .unwrap_or_default())
2298}
2299
2300fn collect_dependencies(
2301 dt: &DataType,
2302 types: &Types,
2303 deps: &mut HashSet<TypeIdentity>,
2304) -> Result<(), Error> {
2305 match dt {
2306 DataType::Struct(s) => {
2307 collect_conversion_dependencies(&s.attributes, types, deps)?;
2308 collect_fields_dependencies(&s.fields, types, deps)?;
2309 }
2310 DataType::Enum(e) => {
2311 collect_conversion_dependencies(&e.attributes, types, deps)?;
2312 for (_, variant) in &e.variants {
2313 collect_fields_dependencies(&variant.fields, types, deps)?;
2314 }
2315 }
2316 DataType::Tuple(tuple) => {
2317 for ty in &tuple.elements {
2318 collect_dependencies(ty, types, deps)?;
2319 }
2320 }
2321 DataType::List(list) => collect_dependencies(&list.ty, types, deps)?,
2322 DataType::Map(map) => {
2323 collect_dependencies(map.key_ty(), types, deps)?;
2324 collect_dependencies(map.value_ty(), types, deps)?;
2325 }
2326 DataType::Intersection(types_) => {
2327 for ty in types_ {
2328 collect_dependencies(ty, types, deps)?;
2329 }
2330 }
2331 DataType::Nullable(inner) => collect_dependencies(inner, types, deps)?,
2332 DataType::Reference(Reference::Named(reference)) => {
2333 if let NamedReferenceType::Inline { dt, .. } = &reference.inner {
2334 collect_dependencies(dt, types, deps)?;
2335 }
2336
2337 if let Some(referenced) = types.get(reference) {
2338 deps.insert(TypeIdentity::from_ndt(referenced));
2339 }
2340
2341 for (_, generic) in named_reference_generics(reference) {
2342 collect_dependencies(generic, types, deps)?;
2343 }
2344 }
2345 DataType::Reference(Reference::Opaque(_)) => {
2346 if let DataType::Reference(Reference::Opaque(reference)) = dt
2347 && let Some(phased) = reference.downcast_ref::<PhasedTy>()
2348 {
2349 collect_dependencies(&phased.serialize, types, deps)?;
2350 collect_dependencies(&phased.deserialize, types, deps)?;
2351 }
2352 }
2353 DataType::Primitive(_) | DataType::Generic(_) => {}
2354 }
2355
2356 Ok(())
2357}
2358
2359fn collect_conversion_dependencies(
2360 attrs: &specta::datatype::Attributes,
2361 types: &Types,
2362 deps: &mut HashSet<TypeIdentity>,
2363) -> Result<(), Error> {
2364 let Some(conversions) = SerdeContainerAttrs::from_attributes(attrs)? else {
2365 return Ok(());
2366 };
2367
2368 for conversion in [
2369 conversions.resolved_into.as_ref(),
2370 conversions.resolved_from.as_ref(),
2371 conversions.resolved_try_from.as_ref(),
2372 ]
2373 .into_iter()
2374 .flatten()
2375 {
2376 collect_dependencies(conversion, types, deps)?;
2377 }
2378
2379 Ok(())
2380}
2381
2382fn collect_fields_dependencies(
2383 fields: &Fields,
2384 types: &Types,
2385 deps: &mut HashSet<TypeIdentity>,
2386) -> Result<(), Error> {
2387 match fields {
2388 Fields::Unit => {}
2389 Fields::Unnamed(unnamed) => {
2390 for field in &unnamed.fields {
2391 if let Some(ty) = field.ty.as_ref() {
2392 collect_dependencies(ty, types, deps)?;
2393 }
2394 }
2395 }
2396 Fields::Named(named) => {
2397 for (_, field) in &named.fields {
2398 if let Some(ty) = field.ty.as_ref() {
2399 collect_dependencies(ty, types, deps)?;
2400 }
2401 }
2402 }
2403 }
2404
2405 Ok(())
2406}
2407
2408fn build_from_original(
2409 original: &NamedDataType,
2410 mode: PhaseRewrite,
2411) -> Result<NamedDataType, Error> {
2412 let mut ndt = original.clone();
2413 ndt.name = Cow::Owned(split_type_name(original, mode)?);
2414
2415 Ok(ndt)
2416}
2417
2418fn register_generated_type(types: &mut Types, generated: NamedDataType) -> NamedDataType {
2419 NamedDataType::new(generated.name.clone(), types, move |_, ndt| {
2420 ndt.docs = generated.docs;
2421 ndt.deprecated = generated.deprecated;
2422 ndt.module_path = generated.module_path;
2423 ndt.location = generated.location;
2424 ndt.generics = generated.generics;
2425 ndt.ty = generated.ty;
2426 })
2427}
2428
2429fn rewrite_named_type_for_phase(ndt: &mut NamedDataType, mode: PhaseRewrite) -> Result<(), Error> {
2430 if let Some(ty) = &ndt.ty
2431 && let Some(rename) = renamed_type_name_for_phase(ty, mode, ndt.name.as_ref())?
2432 {
2433 ndt.name = Cow::Owned(rename);
2434 }
2435
2436 Ok(())
2437}
2438
2439fn split_type_name(original: &NamedDataType, mode: PhaseRewrite) -> Result<String, Error> {
2440 let suffix = match mode {
2441 PhaseRewrite::Serialize => "Serialize",
2442 PhaseRewrite::Deserialize => "Deserialize",
2443 PhaseRewrite::Unified => return Ok(original.name.to_string()),
2444 };
2445
2446 let base_name = original
2447 .ty
2448 .as_ref()
2449 .map(|ty| renamed_type_name_for_phase(ty, mode, original.name.as_ref()))
2450 .transpose()?
2451 .flatten()
2452 .unwrap_or_else(|| original.name.to_string());
2453
2454 Ok(format!("{base_name}_{suffix}"))
2455}
2456
2457fn renamed_type_name_for_phase(
2458 ty: &DataType,
2459 mode: PhaseRewrite,
2460 current_name: &str,
2461) -> Result<Option<String>, Error> {
2462 let DataType::Struct(strct) = ty else {
2463 return Ok(None);
2464 };
2465 let Some(attrs) = SerdeContainerAttrs::from_attributes(&strct.attributes)? else {
2466 return Ok(None);
2467 };
2468
2469 Ok(select_phase_string(
2470 mode,
2471 attrs.rename_serialize.as_deref(),
2472 attrs.rename_deserialize.as_deref(),
2473 "container rename",
2474 current_name,
2475 )?
2476 .map(str::to_string))
2477}
2478
2479fn apply_field_attrs(
2480 field: &mut Field,
2481 mode: PhaseRewrite,
2482 container_default: bool,
2483) -> Result<(), Error> {
2484 let mut optional = field.optional;
2485 if let Some(attrs) = SerdeFieldAttrs::from_attributes(&field.attributes)? {
2486 if field_is_optional_for_mode(Some(&attrs), container_default, mode) {
2487 optional = true;
2488 }
2489 } else if field_is_optional_for_mode(None, container_default, mode) {
2490 optional = true;
2491 }
2492 field.optional = optional;
2493
2494 Ok(())
2495}
2496
2497fn field_is_optional_for_mode(
2498 attrs: Option<&SerdeFieldAttrs>,
2499 container_default: bool,
2500 mode: PhaseRewrite,
2501) -> bool {
2502 match mode {
2503 PhaseRewrite::Serialize => false,
2504 PhaseRewrite::Deserialize | PhaseRewrite::Unified => {
2505 container_default
2506 || attrs.is_some_and(|attrs| attrs.default || attrs.skip_deserializing)
2507 }
2508 }
2509}
2510
2511#[cfg(test)]
2512mod tests {
2513 #![allow(clippy::panic)]
2514
2515 use serde::{Deserialize, Serialize};
2516 use specta::{Format as _, Type, Types, datatype::DataType};
2517
2518 use super::{
2519 Phase, Phased, PhasesFormat, parser, select_phase_datatype,
2520 validate::{ApplyMode, validate_datatype_for_mode},
2521 };
2522
2523 #[derive(Type, Serialize, Deserialize)]
2524 #[serde(untagged)]
2525 enum OneOrManyString {
2526 One(String),
2527 Many(Vec<String>),
2528 }
2529
2530 #[derive(Type, Serialize, Deserialize)]
2531 struct Filters {
2532 #[specta(type = Phased<Vec<String>, OneOrManyString>)]
2533 tags: Vec<String>,
2534 }
2535
2536 #[derive(Type, Serialize, Deserialize)]
2537 struct FilterList {
2538 items: Vec<Filters>,
2539 }
2540
2541 #[derive(Type, Serialize, Deserialize)]
2542 struct Plain {
2543 name: String,
2544 }
2545
2546 #[derive(Type, Serialize, Deserialize)]
2547 struct WithSkipIf {
2548 #[serde(default, skip_serializing_if = "Option::is_none")]
2549 nickname: Option<String>,
2550 }
2551
2552 #[test]
2553 fn selects_split_named_reference_for_each_phase() {
2554 let mut types = specta::Types::default();
2555 let dt = Filters::definition(&mut types);
2556 let resolved = formatted_phases(types);
2557
2558 let serialize = select_phase_datatype(&dt, &resolved, Phase::Serialize);
2559 let deserialize = select_phase_datatype(&dt, &resolved, Phase::Deserialize);
2560
2561 assert_named_reference(&serialize, &resolved, "Filters_Serialize");
2562 assert_named_reference(&deserialize, &resolved, "Filters_Deserialize");
2563 }
2564
2565 #[test]
2566 fn rewrites_nested_generics_for_each_phase() {
2567 let mut types = specta::Types::default();
2568 let dt = FilterList::definition(&mut types);
2569 let resolved = formatted_phases(types);
2570
2571 let serialize = select_phase_datatype(&dt, &resolved, Phase::Serialize);
2572 let deserialize = select_phase_datatype(&dt, &resolved, Phase::Deserialize);
2573
2574 assert_named_reference(&serialize, &resolved, "FilterList_Serialize");
2575 assert_named_reference(&deserialize, &resolved, "FilterList_Deserialize");
2576
2577 let serialize_inner = named_field_type(&serialize, &resolved, "items");
2578 let deserialize_inner = named_field_type(&deserialize, &resolved, "items");
2579
2580 assert_named_reference(
2581 list_item_type(serialize_inner),
2582 &resolved,
2583 "Filters_Serialize",
2584 );
2585 assert_named_reference(
2586 list_item_type(deserialize_inner),
2587 &resolved,
2588 "Filters_Deserialize",
2589 );
2590 }
2591
2592 #[test]
2593 fn preserves_unsplit_types() {
2594 let mut types = specta::Types::default();
2595 let dt = Plain::definition(&mut types);
2596 let resolved = formatted_phases(types);
2597
2598 let serialize = select_phase_datatype(&dt, &resolved, Phase::Serialize);
2599 let deserialize = select_phase_datatype(&dt, &resolved, Phase::Deserialize);
2600
2601 assert_named_reference(&serialize, &resolved, "Plain");
2602 assert_named_reference(&deserialize, &resolved, "Plain");
2603 }
2604
2605 #[test]
2606 fn clears_skip_serializing_if_attribute_after_phase_split() {
2607 let mut types = specta::Types::default();
2608 let dt = WithSkipIf::definition(&mut types);
2609 let resolved = formatted_phases(types);
2610
2611 let serialize = select_phase_datatype(&dt, &resolved, Phase::Serialize);
2612 let deserialize = select_phase_datatype(&dt, &resolved, Phase::Deserialize);
2613
2614 assert!(!field_has_skip_serializing_if(
2615 &serialize, &resolved, "nickname"
2616 ));
2617 assert!(!field_has_skip_serializing_if(
2618 &deserialize,
2619 &resolved,
2620 "nickname"
2621 ));
2622 }
2623
2624 #[test]
2625 fn phase_split_field_passes_unified_mode_validation() {
2626 let mut types = specta::Types::default();
2633 let dt = WithSkipIf::definition(&mut types);
2634 let resolved = formatted_phases(types);
2635
2636 let serialize = select_phase_datatype(&dt, &resolved, Phase::Serialize);
2637 let deserialize = select_phase_datatype(&dt, &resolved, Phase::Deserialize);
2638
2639 validate_datatype_for_mode(&serialize, &resolved, ApplyMode::Unified)
2640 .expect("Unified validation should accept phase-split _Serialize variant");
2641 validate_datatype_for_mode(&deserialize, &resolved, ApplyMode::Unified)
2642 .expect("Unified validation should accept phase-split _Deserialize variant");
2643 }
2644
2645 #[test]
2646 fn resolves_explicit_phased_datatypes_without_named_types() {
2647 let mut types = specta::Types::default();
2648 let dt = <Phased<String, Vec<String>>>::definition(&mut types);
2649 let resolved = formatted_phases(types);
2650
2651 let serialize = select_phase_datatype(&dt, &resolved, Phase::Serialize);
2652 let deserialize = select_phase_datatype(&dt, &resolved, Phase::Deserialize);
2653
2654 assert_named_reference(&serialize, &resolved, "String");
2655 assert_named_reference(&deserialize, &resolved, "Vec");
2656 }
2657
2658 fn assert_named_reference(dt: &DataType, types: &Types, expected_name: &str) {
2659 let DataType::Reference(specta::datatype::Reference::Named(reference)) = dt else {
2660 panic!("expected named reference");
2661 };
2662
2663 let actual = types
2664 .get(reference)
2665 .expect("reference should resolve")
2666 .name
2667 .as_ref();
2668
2669 assert_eq!(actual, expected_name);
2670 }
2671
2672 fn named_field_type<'a>(dt: &'a DataType, types: &'a Types, field_name: &str) -> &'a DataType {
2673 let DataType::Reference(specta::datatype::Reference::Named(reference)) = dt else {
2674 panic!("expected named reference");
2675 };
2676
2677 let named = types.get(reference).expect("reference should resolve");
2678 let Some(DataType::Struct(strct)) = &named.ty else {
2679 panic!("expected struct type");
2680 };
2681 let specta::datatype::Fields::Named(fields) = &strct.fields else {
2682 panic!("expected named fields");
2683 };
2684
2685 fields
2686 .fields
2687 .iter()
2688 .find_map(|(name, field)| (name == field_name).then_some(field.ty.as_ref()).flatten())
2689 .expect("field should exist")
2690 }
2691
2692 fn field_has_skip_serializing_if(dt: &DataType, types: &Types, field_name: &str) -> bool {
2693 let DataType::Reference(specta::datatype::Reference::Named(reference)) = dt else {
2694 panic!("expected named reference");
2695 };
2696 let named = types.get(reference).expect("reference should resolve");
2697 let Some(DataType::Struct(strct)) = &named.ty else {
2698 panic!("expected struct type");
2699 };
2700 let specta::datatype::Fields::Named(fields) = &strct.fields else {
2701 panic!("expected named fields");
2702 };
2703 fields
2704 .fields
2705 .iter()
2706 .find(|(name, _)| name == field_name)
2707 .map(|(_, field)| {
2708 field
2709 .attributes
2710 .contains_key(parser::FIELD_SKIP_SERIALIZING_IF)
2711 })
2712 .expect("field should exist")
2713 }
2714
2715 fn first_generic_type(dt: &DataType) -> &DataType {
2716 let DataType::Reference(specta::datatype::Reference::Named(reference)) = dt else {
2717 panic!("expected named reference with generics");
2718 };
2719
2720 let specta::datatype::NamedReferenceType::Reference { generics, .. } = &reference.inner
2721 else {
2722 panic!("expected named reference with generics");
2723 };
2724
2725 generics
2726 .first()
2727 .map(|(_, dt)| dt)
2728 .expect("expected first generic type")
2729 }
2730
2731 fn list_item_type(dt: &DataType) -> &DataType {
2732 let DataType::Reference(specta::datatype::Reference::Named(reference)) = dt else {
2733 panic!("expected inline list reference");
2734 };
2735
2736 let specta::datatype::NamedReferenceType::Inline { dt, .. } = &reference.inner else {
2737 return first_generic_type(dt);
2738 };
2739
2740 let DataType::List(list) = dt.as_ref() else {
2741 panic!("expected inline list");
2742 };
2743
2744 &list.ty
2745 }
2746
2747 fn formatted_phases(types: Types) -> Types {
2748 let format = PhasesFormat;
2749 format
2750 .map_types(&types)
2751 .expect("PhasesFormat should succeed")
2752 .into_owned()
2753 }
2754}