1#![deny(unsafe_code)]
2use proc_macro::TokenStream;
138use proc_macro2::TokenStream as TokenStream2;
139use quote::quote;
140use syn::{Data, DeriveInput, Field, Fields, Type, parse_macro_input, spanned::Spanned};
141
142#[proc_macro_derive(EdifactSerialize, attributes(edifact))]
145pub fn derive_edifact_serialize(input: TokenStream) -> TokenStream {
154 let input = parse_macro_input!(input as DeriveInput);
155 impl_serialize(&input)
156 .unwrap_or_else(|e| e.to_compile_error())
157 .into()
158}
159
160#[proc_macro_derive(EdifactDeserialize, attributes(edifact))]
161pub fn derive_edifact_deserialize(input: TokenStream) -> TokenStream {
170 let input = parse_macro_input!(input as DeriveInput);
171 impl_deserialize(&input)
172 .unwrap_or_else(|e| e.to_compile_error())
173 .into()
174}
175
176#[proc_macro_derive(EdifactCompositeDeserialize, attributes(edifact))]
177pub fn derive_edifact_composite_deserialize(input: TokenStream) -> TokenStream {
192 let input = parse_macro_input!(input as DeriveInput);
193 impl_composite_deserialize(&input)
194 .unwrap_or_else(|e| e.to_compile_error())
195 .into()
196}
197
198#[proc_macro_derive(EdifactCompositeSerialize, attributes(edifact))]
199pub fn derive_edifact_composite_serialize(input: TokenStream) -> TokenStream {
210 let input = parse_macro_input!(input as DeriveInput);
211 impl_composite_serialize(&input)
212 .unwrap_or_else(|e| e.to_compile_error())
213 .into()
214}
215
216#[derive(Clone)]
225enum Position {
226 Index(u32),
228 Code(String),
230}
231
232#[derive(Default)]
233struct StructAttrs {
234 segment: Option<String>,
236 qualifier: Option<String>,
238 qualifier_span: Option<proc_macro2::Span>,
239 qualifier_from: Option<u32>,
241 qualifier_from_span: Option<proc_macro2::Span>,
242 layout: Option<syn::Path>,
245 layout_span: Option<proc_macro2::Span>,
246}
247
248#[derive(Default)]
249struct FieldAttrs {
250 element: Option<Position>,
252 element_span: Option<proc_macro2::Span>,
253 component: Option<u32>,
255 component_span: Option<proc_macro2::Span>,
256 composite: bool,
258 composite_span: Option<proc_macro2::Span>,
259 group: bool,
261 group_span: Option<proc_macro2::Span>,
262 qualifier: Option<String>,
264 qualifier_span: Option<proc_macro2::Span>,
265 required: bool,
273 required_span: Option<proc_macro2::Span>,
274 repeat: bool,
282 repeat_span: Option<proc_macro2::Span>,
283}
284
285const MAX_POSITION_INDEX: u32 = 256;
295
296fn check_index_bound(lit: &syn::LitInt, value: u32, key: &str) -> syn::Result<()> {
297 if value > MAX_POSITION_INDEX {
298 return Err(syn::Error::new(
299 lit.span(),
300 format!(
301 "`{key}` index {value} exceeds the maximum of {MAX_POSITION_INDEX}; \
302 UN/EDIFACT allows at most 99 elements per segment and 99 components per composite"
303 ),
304 ));
305 }
306 Ok(())
307}
308
309fn parse_struct_attrs(input: &DeriveInput) -> syn::Result<StructAttrs> {
310 let mut out = StructAttrs::default();
311 for attr in &input.attrs {
312 if !attr.path().is_ident("edifact") {
313 continue;
314 }
315 attr.parse_nested_meta(|meta| {
316 if meta.path.is_ident("segment") {
317 if out.segment.is_some() {
318 return Err(meta.error("duplicate `segment`"));
319 }
320 let lit = meta.value()?.parse::<syn::LitStr>()?;
321 let tag = lit.value();
322 if tag.len() != 3 || !tag.bytes().all(|b| b.is_ascii_uppercase()) {
323 return Err(syn::Error::new(
324 lit.span(),
325 format!(
326 "segment tag must be exactly 3 ASCII uppercase letters; got {tag:?}"
327 ),
328 ));
329 }
330 out.segment = Some(tag);
331 } else if meta.path.is_ident("qualifier") {
332 if out.qualifier.is_some() {
333 return Err(meta.error("duplicate `qualifier`"));
334 }
335 out.qualifier = Some(meta.value()?.parse::<syn::LitStr>()?.value());
336 out.qualifier_span = Some(meta.path.span());
337 } else if meta.path.is_ident("qualifier_from") {
338 if out.qualifier_from.is_some() {
339 return Err(meta.error("duplicate `qualifier_from`"));
340 }
341 let lit = meta.value()?.parse::<syn::LitInt>()?;
342 let idx: u32 = lit.base10_parse()?;
343 check_index_bound(&lit, idx, "qualifier_from")?;
344 out.qualifier_from = Some(idx);
345 out.qualifier_from_span = Some(meta.path.span());
346 } else if meta.path.is_ident("layout") {
347 if out.layout.is_some() {
348 return Err(meta.error("duplicate `layout`"));
349 }
350 out.layout_span = Some(meta.path.span());
351 let value = meta.value()?;
352 out.layout = Some(if value.peek(syn::LitStr) {
356 value.parse::<syn::LitStr>()?.parse()?
357 } else {
358 value.parse::<syn::Path>()?
359 });
360 } else {
361 return Err(meta.error("unknown struct-level `edifact` key; expected `segment`, `qualifier`, `qualifier_from`, or `layout`"));
362 }
363 Ok(())
364 })?;
365 }
366 if out.layout.is_some() && out.segment.is_none() {
367 return Err(syn::Error::new(
368 out.layout_span.unwrap_or_else(|| input.span()),
369 "#[edifact(layout = ...)] requires #[edifact(segment = ...)]: a layout describes one segment",
370 ));
371 }
372 if (out.qualifier.is_some() || out.qualifier_from.is_some()) && out.segment.is_none() {
373 return Err(syn::Error::new(
374 out.qualifier_span
375 .or(out.qualifier_from_span)
376 .unwrap_or_else(|| input.span()),
377 "#[edifact(qualifier = ...)] / #[edifact(qualifier_from = ...)] require #[edifact(segment = ...)]",
378 ));
379 }
380 if out.qualifier.is_some() && out.qualifier_from.is_some() {
381 return Err(syn::Error::new(
382 out.qualifier_from_span
383 .or(out.qualifier_span)
384 .unwrap_or_else(|| input.span()),
385 "use either #[edifact(qualifier = ...)] or #[edifact(qualifier_from = ...)], not both",
386 ));
387 }
388 Ok(out)
389}
390
391fn parse_field_attrs(field: &Field) -> syn::Result<FieldAttrs> {
392 let mut out = FieldAttrs::default();
393 for attr in &field.attrs {
394 if !attr.path().is_ident("edifact") {
395 continue;
396 }
397 attr.parse_nested_meta(|meta| {
398 if meta.path.is_ident("element") {
399 if out.element.is_some() {
400 return Err(meta.error("duplicate `element`"));
401 }
402 out.element_span = Some(meta.path.span());
403 let value = meta.value()?;
404 out.element = Some(if value.peek(syn::LitStr) {
405 let lit = value.parse::<syn::LitStr>()?;
406 let code = lit.value();
407 if code.is_empty() {
408 return Err(syn::Error::new(
409 lit.span(),
410 "`element` data element identifier must not be empty",
411 ));
412 }
413 Position::Code(code)
414 } else {
415 let lit = value.parse::<syn::LitInt>()?;
416 let idx: u32 = lit.base10_parse()?;
417 check_index_bound(&lit, idx, "element")?;
418 Position::Index(idx)
419 });
420 } else if meta.path.is_ident("component") {
421 if out.component.is_some() {
422 return Err(meta.error("duplicate `component`"));
423 }
424 out.component_span = Some(meta.path.span());
425 let value = meta.value()?;
426 if value.peek(syn::LitStr) {
427 let lit = value.parse::<syn::LitStr>()?;
428 return Err(syn::Error::new(
429 lit.span(),
430 "put the data element identifier in `element`: \
431 `#[edifact(element = \"3055\")]` resolves both the element and the \
432 component position from the directory",
433 ));
434 }
435 let lit = value.parse::<syn::LitInt>()?;
436 let idx: u32 = lit.base10_parse()?;
437 check_index_bound(&lit, idx, "component")?;
438 out.component = Some(idx);
439 } else if meta.path.is_ident("composite") {
440 out.composite = true;
441 out.composite_span = Some(meta.path.span());
442 } else if meta.path.is_ident("group") {
443 out.group = true;
444 out.group_span = Some(meta.path.span());
445 } else if meta.path.is_ident("qualifier") {
446 if out.qualifier.is_some() {
447 return Err(meta.error("duplicate `qualifier`"));
448 }
449 out.qualifier = Some(meta.value()?.parse::<syn::LitStr>()?.value());
450 out.qualifier_span = Some(meta.path.span());
451 } else if meta.path.is_ident("required") {
452 out.required = true;
453 out.required_span = Some(meta.path.span());
454 } else if meta.path.is_ident("repeat") {
455 out.repeat = true;
456 out.repeat_span = Some(meta.path.span());
457 } else {
458 return Err(meta.error("unknown field-level `edifact` key; expected `element`, `component`, `composite`, `group`, `qualifier`, `repeat`, or `required`"));
459 }
460 Ok(())
461 })?;
462 }
463 Ok(out)
464}
465
466struct Slots {
470 element: TokenStream2,
472 component: TokenStream2,
474 has_component: bool,
480 names_component: TokenStream2,
491}
492
493fn resolve_slots(
501 struct_attrs: &StructAttrs,
502 field_data: &[(&syn::Ident, &Type, FieldAttrs)],
503) -> syn::Result<(TokenStream2, Vec<Slots>)> {
504 let mut consts: Vec<TokenStream2> = Vec::new();
505 let mut slots: Vec<Slots> = Vec::with_capacity(field_data.len());
506 let mut slot_entries: Vec<TokenStream2> = Vec::new();
507
508 if struct_attrs.qualifier.is_some() {
509 slot_entries.push(quote! { (0usize, 0usize) });
511 }
512
513 for (i, (ident, _, attrs)) in field_data.iter().enumerate() {
514 if attrs.group {
515 slots.push(Slots {
516 element: quote! { 0usize },
517 component: quote! { 0usize },
518 has_component: false,
519 names_component: quote! { false },
520 });
521 continue;
522 }
523 let component_index = attrs.component.unwrap_or(0);
524 let slot = match &attrs.element {
525 Some(Position::Code(code)) => {
526 let Some(layout) = &struct_attrs.layout else {
527 return Err(syn::Error::new(
528 attrs.element_span.unwrap_or_else(|| ident.span()),
529 format!(
530 "field `{ident}`: `element = \"{code}\"` addresses a UN/EDIFACT data \
531 element identifier, which needs a directory to resolve against; add \
532 #[edifact(layout = path::to::SEGMENT_DEFINITION)] to the struct"
533 ),
534 ));
535 };
536 let slot_ident = syn::Ident::new(
537 &format!("__EDIFACT_SLOT_{i}"),
538 attrs.element_span.unwrap_or_else(|| ident.span()),
539 );
540 let unknown_msg = format!(
541 "field `{ident}`: data element {code} is not defined exactly once in the \
542 segment layout — check the identifier against the directory"
543 );
544 consts.push(quote! {
545 const _: () = ::core::assert!(#layout.code_positions(#code) == 1, #unknown_msg);
546 });
547 if attrs.component.is_some() {
548 let conflict_msg = format!(
549 "field `{ident}`: `component = N` may only accompany an identifier that \
550 names a whole data element, but {code} names a component inside one"
551 );
552 consts.push(quote! {
553 const _: () =
554 ::core::assert!(#layout.component_slot(#code) == 0, #conflict_msg);
555 });
556 consts.push(quote! {
557 const #slot_ident: (usize, usize) =
561 if #layout.code_positions(#code) == 1 {
562 (#layout.element_slot(#code), #component_index as usize)
563 } else {
564 (0, 0)
565 };
566 });
567 } else {
568 consts.push(quote! {
569 const #slot_ident: (usize, usize) =
570 if #layout.code_positions(#code) == 1 {
571 (#layout.element_slot(#code), #layout.component_slot(#code))
572 } else {
573 (0, 0)
574 };
575 });
576 }
577 slot_entries.push(quote! { #slot_ident });
578 let names_component = if attrs.component.is_some() {
579 quote! { true }
582 } else {
583 quote! { #layout.code_is_component(#code) }
584 };
585 Slots {
586 element: quote! { #slot_ident.0 },
587 component: quote! { #slot_ident.1 },
588 has_component: true,
589 names_component,
590 }
591 }
592 Some(Position::Index(idx)) => {
593 let idx = *idx;
594 let explicit = attrs.component.is_some();
595 slot_entries.push(quote! { (#idx as usize, #component_index as usize) });
596 Slots {
597 element: quote! { #idx as usize },
598 component: quote! { #component_index as usize },
599 has_component: explicit,
600 names_component: quote! { #explicit },
601 }
602 }
603 None => {
604 let idx = i as u32;
606 let explicit = attrs.component.is_some();
607 slot_entries.push(quote! { (#idx as usize, #component_index as usize) });
608 Slots {
609 element: quote! { #idx as usize },
610 component: quote! { #component_index as usize },
611 has_component: explicit,
612 names_component: quote! { #explicit },
613 }
614 }
615 };
616 slots.push(slot);
617 }
618
619 if struct_attrs.layout.is_some() && !slot_entries.is_empty() {
624 let count = slot_entries.len();
625 consts.push(quote! {
626 const __EDIFACT_SLOTS: [(usize, usize); #count] = [#(#slot_entries),*];
627 const _: () = {
628 let mut i = 0;
629 while i < __EDIFACT_SLOTS.len() {
630 let mut j = i + 1;
631 while j < __EDIFACT_SLOTS.len() {
632 ::core::assert!(
633 !(__EDIFACT_SLOTS[i].0 == __EDIFACT_SLOTS[j].0
634 && __EDIFACT_SLOTS[i].1 == __EDIFACT_SLOTS[j].1),
635 "two fields resolve to the same element/component slot; \
636 give each field a distinct data element identifier or index"
637 );
638 j += 1;
639 }
640 i += 1;
641 }
642 };
643 });
644 }
645
646 Ok((quote! { #(#consts)* }, slots))
647}
648
649fn is_option_type(ty: &Type) -> bool {
652 matches!(ty, Type::Path(p) if p.path.segments.last().is_some_and(|s| s.ident == "Option"))
653}
654
655fn is_vec_type(ty: &Type) -> bool {
656 matches!(ty, Type::Path(p) if p.path.segments.last().is_some_and(|s| s.ident == "Vec"))
657}
658
659fn is_string_type(ty: &Type) -> bool {
677 let Type::Path(p) = ty else { return false };
678 if p.path.is_ident("String") {
680 return true;
681 }
682 let segs = &p.path.segments;
684 segs.len() == 3
685 && (segs[0].ident == "std" || segs[0].ident == "alloc")
686 && segs[1].ident == "string"
687 && segs[2].ident == "String"
688}
689
690fn is_str_ref_type(ty: &Type) -> bool {
692 let Type::Reference(r) = ty else { return false };
693 matches!(r.elem.as_ref(), Type::Path(p) if p.path.is_ident("str"))
694}
695
696fn is_str_like(ty: &Type) -> bool {
699 is_string_type(ty) || is_str_ref_type(ty)
700}
701
702fn option_inner_type(ty: &Type) -> Option<&Type> {
703 let Type::Path(path) = ty else { return None };
704 let seg = path.path.segments.last()?;
705 if seg.ident != "Option" {
706 return None;
707 }
708 let syn::PathArguments::AngleBracketed(args) = &seg.arguments else {
709 return None;
710 };
711 let syn::GenericArgument::Type(inner) = args.args.first()? else {
712 return None;
713 };
714 Some(inner)
715}
716
717fn vec_inner_type(ty: &Type) -> Option<&Type> {
718 let Type::Path(path) = ty else { return None };
719 let seg = path.path.segments.last()?;
720 if seg.ident != "Vec" {
721 return None;
722 }
723 let syn::PathArguments::AngleBracketed(args) = &seg.arguments else {
724 return None;
725 };
726 let syn::GenericArgument::Type(inner) = args.args.first()? else {
727 return None;
728 };
729 Some(inner)
730}
731
732fn check_duplicate_slots(
744 field_data: &[(&syn::Ident, &Type, FieldAttrs)],
745 is_segment_struct: bool,
746) -> syn::Result<()> {
747 if !is_segment_struct {
748 return Ok(());
749 }
750 let mut seen: Vec<((u32, u32), &syn::Ident)> = Vec::with_capacity(field_data.len());
751 for (i, (ident, _, attrs)) in field_data.iter().enumerate() {
752 if attrs.group {
754 continue;
755 }
756 let element = match &attrs.element {
757 Some(Position::Index(idx)) => *idx,
758 Some(Position::Code(_)) => continue,
759 None => i as u32,
760 };
761 let slot = (element, attrs.component.unwrap_or(0));
762 if let Some((_, first)) = seen.iter().find(|(s, _)| *s == slot) {
763 return Err(syn::Error::new(
764 ident.span(),
765 format!(
766 "field `{ident}` maps to element {} component {}, which is already \
767 claimed by field `{first}`; give each field a distinct \
768 `#[edifact(element = ..., component = ...)]` slot",
769 slot.0, slot.1
770 ),
771 ));
772 }
773 seen.push((slot, ident));
774 }
775 Ok(())
776}
777
778struct CompositeSlot<'a> {
782 ident: &'a syn::Ident,
783 ty: &'a Type,
784 index: u32,
785 optional: bool,
786}
787
788fn composite_slots(input: &DeriveInput) -> syn::Result<Vec<CompositeSlot<'_>>> {
795 let fields = get_named_fields(input)?;
796 let mut slots: Vec<CompositeSlot<'_>> = Vec::with_capacity(fields.named.len());
797
798 for (decl_index, field) in fields.named.iter().enumerate() {
799 let ident = field.ident.as_ref().expect("named fields checked above");
800 let attrs = parse_field_attrs(field)?;
801
802 for (present, span, key) in [
803 (attrs.element.is_some(), attrs.element_span, "element"),
804 (attrs.composite, attrs.composite_span, "composite"),
805 (attrs.group, attrs.group_span, "group"),
806 (attrs.qualifier.is_some(), attrs.qualifier_span, "qualifier"),
807 ] {
808 if present {
809 return Err(syn::Error::new(
810 span.unwrap_or_else(|| field.span()),
811 format!(
812 "`{key}` has no meaning on a composite struct field; \
813 a composite maps its fields to components, so only \
814 `component` and `required` apply"
815 ),
816 ));
817 }
818 }
819
820 let ty = &field.ty;
821 let inner = option_inner_type(ty).unwrap_or(ty);
822 if !is_string_type(inner) {
823 return Err(syn::Error::new(
824 ty.span(),
825 "composite struct fields must be `String` or `Option<String>`; \
826 a component is a single text value",
827 ));
828 }
829
830 slots.push(CompositeSlot {
831 ident,
832 ty,
833 index: attrs.component.unwrap_or(decl_index as u32),
834 optional: is_option_type(ty) && !attrs.required,
835 });
836 }
837
838 let mut seen: Vec<(u32, &syn::Ident)> = Vec::with_capacity(slots.len());
841 for slot in &slots {
842 if let Some((_, first)) = seen.iter().find(|(i, _)| *i == slot.index) {
843 return Err(syn::Error::new(
844 slot.ident.span(),
845 format!(
846 "component {} is already mapped by field `{first}`",
847 slot.index
848 ),
849 ));
850 }
851 seen.push((slot.index, slot.ident));
852 }
853 Ok(slots)
854}
855
856fn impl_composite_deserialize(input: &DeriveInput) -> syn::Result<TokenStream2> {
857 let name = &input.ident;
858 let slots = composite_slots(input)?;
859
860 let assignments = slots.iter().map(|slot| {
861 let CompositeSlot {
862 ident,
863 ty,
864 index,
865 optional,
866 } = slot;
867 let idx = *index as usize;
868 if *optional {
869 quote! {
870 #ident: match __composite.get(#idx) {
871 Some(v) if !v.is_empty() => Some(::std::string::String::from(v)),
872 _ => None,
873 },
874 }
875 } else {
876 let build = if is_option_type(ty) {
879 quote! { Some(::std::string::String::from(__value)) }
880 } else {
881 quote! { ::std::string::String::from(__value) }
882 };
883 quote! {
884 #ident: {
885 let __value = __composite
886 .get(#idx)
887 .filter(|v| !v.is_empty())
888 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredComponent {
889 tag: ::std::string::String::from(stringify!(#name)),
890 element_index: 0,
891 component_index: #idx,
892 })?;
893 #build
894 },
895 }
896 }
897 });
898
899 Ok(quote! {
900 impl ::edifact_rs::EdifactCompositeDeserialize for #name {
901 fn edifact_deserialize_composite(
902 __composite: ::edifact_rs::CompositeElement<'_>,
903 ) -> ::core::result::Result<Self, ::edifact_rs::EdifactError> {
904 ::core::result::Result::Ok(Self {
905 #(#assignments)*
906 })
907 }
908 }
909 })
910}
911
912fn impl_composite_serialize(input: &DeriveInput) -> syn::Result<TokenStream2> {
913 let name = &input.ident;
914 let slots = composite_slots(input)?;
915
916 let mut ordered: Vec<&CompositeSlot<'_>> = slots.iter().collect();
919 ordered.sort_by_key(|slot| slot.index);
920 let highest = ordered.last().map_or(0, |slot| slot.index);
921
922 let emits = (0..=highest).map(|index| {
923 let value = match ordered.iter().find(|slot| slot.index == index) {
924 Some(slot) => {
925 let ident = slot.ident;
926 if is_option_type(slot.ty) {
927 quote! { self.#ident.as_deref().unwrap_or("") }
928 } else {
929 quote! { self.#ident.as_str() }
930 }
931 }
932 None => quote! { "" },
933 };
934 if index == 0 {
936 quote! { __emitter.emit(::edifact_rs::EdifactEvent::element(#value))?; }
937 } else {
938 quote! {
939 __emitter.emit(::edifact_rs::EdifactEvent::component(#value))?;
940 }
941 }
942 });
943
944 let body = if slots.is_empty() {
946 quote! { __emitter.emit(::edifact_rs::EdifactEvent::element(""))?; }
947 } else {
948 quote! { #(#emits)* }
949 };
950
951 Ok(quote! {
952 impl ::edifact_rs::EdifactCompositeSerialize for #name {
953 fn edifact_serialize_composite<__E: ::edifact_rs::EventEmitter>(
954 &self,
955 __emitter: &mut __E,
956 ) -> ::core::result::Result<(), ::edifact_rs::EdifactError> {
957 #body
958 ::core::result::Result::Ok(())
959 }
960 }
961 })
962}
963
964fn get_named_fields(input: &DeriveInput) -> syn::Result<&syn::FieldsNamed> {
965 if !input.generics.params.is_empty() {
966 return Err(syn::Error::new(
967 input.generics.params.span(),
968 "EdifactSerialize/EdifactDeserialize do not support generic structs",
969 ));
970 }
971 match &input.data {
972 Data::Struct(s) => match &s.fields {
973 Fields::Named(f) => Ok(f),
974 _ => Err(syn::Error::new(
975 input.span(),
976 "EdifactSerialize/EdifactDeserialize only support structs with named fields",
977 )),
978 },
979 _ => Err(syn::Error::new(
980 input.span(),
981 "EdifactSerialize/EdifactDeserialize only support structs",
982 )),
983 }
984}
985
986fn validate_field_attrs(
987 ident: &syn::Ident,
988 ty: &Type,
989 attrs: &FieldAttrs,
990 is_segment_struct: bool,
991) -> syn::Result<()> {
992 if attrs.group && !is_vec_type(ty) {
993 return Err(syn::Error::new(
994 attrs.group_span.unwrap_or_else(|| ident.span()),
995 format!("field `{ident}`: #[edifact(group)] requires Vec<T>"),
996 ));
997 }
998 if attrs.group && (attrs.element.is_some() || attrs.component.is_some()) {
999 return Err(syn::Error::new(
1000 attrs.group_span.unwrap_or_else(|| ident.span()),
1001 format!(
1002 "field `{ident}`: #[edifact(group)] cannot be combined with element/component positioning"
1003 ),
1004 ));
1005 }
1006 if attrs.repeat && !is_vec_type(ty) {
1007 return Err(syn::Error::new(
1008 attrs.repeat_span.unwrap_or_else(|| ident.span()),
1009 format!(
1010 "field `{ident}`: #[edifact(repeat)] requires Vec<T> — a repeating data element \
1011 has one occurrence per item"
1012 ),
1013 ));
1014 }
1015 if attrs.repeat && !is_segment_struct {
1016 return Err(syn::Error::new(
1017 attrs.repeat_span.unwrap_or_else(|| ident.span()),
1018 format!(
1019 "field `{ident}`: #[edifact(repeat)] is only valid on segment structs; a message \
1020 struct repeats whole segments, which is #[edifact(group)]"
1021 ),
1022 ));
1023 }
1024 if attrs.repeat && attrs.group {
1025 return Err(syn::Error::new(
1026 attrs.repeat_span.unwrap_or_else(|| ident.span()),
1027 format!(
1028 "field `{ident}`: #[edifact(repeat)] repeats a data element inside one segment; \
1029 #[edifact(group)] repeats the segment itself — they are different structures"
1030 ),
1031 ));
1032 }
1033 if attrs.repeat && attrs.composite {
1034 return Err(syn::Error::new(
1035 attrs.repeat_span.unwrap_or_else(|| ident.span()),
1036 format!(
1037 "field `{ident}`: #[edifact(repeat)] cannot be combined with \
1038 #[edifact(composite)]; map one component across the occurrences instead"
1039 ),
1040 ));
1041 }
1042 if attrs.composite && attrs.component.is_some() {
1043 return Err(syn::Error::new(
1044 attrs.component_span.unwrap_or_else(|| ident.span()),
1045 format!(
1046 "field `{ident}`: #[edifact(component = ...)] cannot be combined with #[edifact(composite)]"
1047 ),
1048 ));
1049 }
1050 if attrs.composite && attrs.group {
1051 return Err(syn::Error::new(
1052 attrs.composite_span.unwrap_or_else(|| ident.span()),
1053 format!(
1054 "field `{ident}`: #[edifact(composite)] cannot be combined with #[edifact(group)]"
1055 ),
1056 ));
1057 }
1058 if is_segment_struct && attrs.group {
1059 return Err(syn::Error::new(
1060 attrs.group_span.unwrap_or_else(|| ident.span()),
1061 format!("field `{ident}`: #[edifact(group)] is only valid on message structs"),
1062 ));
1063 }
1064 if !is_segment_struct && (attrs.element.is_some() || attrs.component.is_some()) {
1065 return Err(syn::Error::new(
1066 attrs
1067 .element_span
1068 .or(attrs.component_span)
1069 .unwrap_or_else(|| ident.span()),
1070 format!(
1071 "field `{ident}`: element/component positioning is only valid on segment structs"
1072 ),
1073 ));
1074 }
1075 if !is_segment_struct && attrs.composite {
1076 return Err(syn::Error::new(
1077 attrs.composite_span.unwrap_or_else(|| ident.span()),
1078 format!("field `{ident}`: #[edifact(composite)] is only valid on segment structs"),
1079 ));
1080 }
1081 if is_segment_struct && attrs.qualifier.is_some() {
1082 return Err(syn::Error::new(
1083 attrs.qualifier_span.unwrap_or_else(|| ident.span()),
1084 format!(
1085 "field `{ident}`: #[edifact(qualifier = ...)] is only valid on message struct fields"
1086 ),
1087 ));
1088 }
1089 if attrs.qualifier.is_some() && attrs.group && !is_vec_type(ty) {
1090 return Err(syn::Error::new(
1091 attrs.qualifier_span.unwrap_or_else(|| ident.span()),
1092 format!("field `{ident}`: qualifier-constrained groups must be Vec<T>"),
1093 ));
1094 }
1095 if attrs.required && !is_option_type(ty) {
1096 return Err(syn::Error::new(
1097 attrs.required_span.unwrap_or_else(|| ident.span()),
1098 format!(
1099 "field `{ident}`: #[edifact(required)] only applies to Option<T> fields; \
1100 non-Option fields are always required"
1101 ),
1102 ));
1103 }
1104 if attrs.required && attrs.composite {
1105 return Err(syn::Error::new(
1106 attrs.required_span.unwrap_or_else(|| ident.span()),
1107 format!(
1108 "field `{ident}`: #[edifact(required)] cannot be combined with \
1109 #[edifact(composite)]; use a non-optional field type to require the \
1110 composite element"
1111 ),
1112 ));
1113 }
1114 if attrs.required && !is_segment_struct {
1115 return Err(syn::Error::new(
1116 attrs.required_span.unwrap_or_else(|| ident.span()),
1117 format!(
1118 "field `{ident}`: #[edifact(required)] is only valid on segment struct \
1119 element fields; to require a segment in a message struct, use a \
1120 non-optional field type"
1121 ),
1122 ));
1123 }
1124 Ok(())
1125}
1126
1127fn static_element_index(attrs: &FieldAttrs, decl_index: usize) -> u32 {
1134 match &attrs.element {
1135 Some(Position::Index(idx)) => *idx,
1136 _ => decl_index as u32,
1137 }
1138}
1139
1140fn impl_serialize_sparse(
1151 name: &syn::Ident,
1152 seg_tag: &str,
1153 struct_attrs: &StructAttrs,
1154 field_data: &[(&syn::Ident, &Type, FieldAttrs)],
1155 slots: &[Slots],
1156 slot_prelude: &TokenStream2,
1157) -> TokenStream2 {
1158 let mut stmts: Vec<TokenStream2> = Vec::new();
1159
1160 if let Some(qual) = &struct_attrs.qualifier {
1161 stmts.push(quote! {
1162 __parts.push((0usize, 0usize, ::std::borrow::Cow::Borrowed(#qual)));
1163 });
1164 }
1165
1166 for ((ident, ty, attrs), slot) in field_data.iter().zip(slots) {
1167 let (element, component) = (&slot.element, &slot.component);
1168 if attrs.composite {
1169 let serialize_composite = if is_option_type(ty) {
1172 quote! {
1173 if let ::core::option::Option::Some(__v) = &self.#ident {
1174 ::edifact_rs::EdifactCompositeSerialize::edifact_serialize_composite(
1175 __v, &mut __sub,
1176 )?;
1177 }
1178 }
1179 } else {
1180 quote! {
1181 ::edifact_rs::EdifactCompositeSerialize::edifact_serialize_composite(
1182 &self.#ident, &mut __sub,
1183 )?;
1184 }
1185 };
1186 stmts.push(quote! {
1187 {
1188 let mut __sub = ::edifact_rs::VecEmitter::default();
1189 #serialize_composite
1190 let mut __comp = 0usize;
1191 let mut __any = false;
1192 for __event in __sub.events {
1193 match __event {
1194 ::edifact_rs::EdifactEvent::Element { value } => {
1195 __comp = 0;
1196 __any = true;
1197 __parts.push((#element, 0usize, ::std::borrow::Cow::Owned(value)));
1198 }
1199 ::edifact_rs::EdifactEvent::ComponentElement { value } => {
1200 __comp += 1;
1201 __any = true;
1202 __parts.push((#element, __comp, ::std::borrow::Cow::Owned(value)));
1203 }
1204 _ => {}
1205 }
1206 }
1207 if !__any {
1208 __parts.push((#element, 0usize, ::std::borrow::Cow::Borrowed("")));
1209 }
1210 }
1211 });
1212 continue;
1213 }
1214
1215 let value_expr = if is_option_type(ty) {
1216 let inner_is_str = option_inner_type(ty).is_some_and(is_str_like);
1217 if inner_is_str {
1218 quote! {
1219 match &self.#ident {
1220 ::core::option::Option::Some(__v) => ::std::borrow::Cow::Borrowed(__v.as_str()),
1221 ::core::option::Option::None => ::std::borrow::Cow::Borrowed(""),
1222 }
1223 }
1224 } else {
1225 quote! {
1226 match &self.#ident {
1227 ::core::option::Option::Some(__v) => {
1228 ::std::borrow::Cow::Owned(::std::string::ToString::to_string(__v))
1229 }
1230 ::core::option::Option::None => ::std::borrow::Cow::Borrowed(""),
1231 }
1232 }
1233 }
1234 } else if is_string_type(ty) {
1235 quote! { ::std::borrow::Cow::Borrowed(self.#ident.as_str()) }
1236 } else if is_str_ref_type(ty) {
1237 quote! { ::std::borrow::Cow::Borrowed(self.#ident) }
1238 } else {
1239 quote! { ::std::borrow::Cow::Owned(::std::string::ToString::to_string(&self.#ident)) }
1240 };
1241
1242 stmts.push(quote! {
1243 __parts.push((#element, #component, #value_expr));
1244 });
1245 }
1246
1247 let capacity = field_data.len() + usize::from(struct_attrs.qualifier.is_some());
1248
1249 quote! {
1250 impl ::edifact_rs::EdifactSerialize for #name {
1251 fn edifact_serialize<__E: ::edifact_rs::EventEmitter>(
1252 &self,
1253 emitter: &mut __E,
1254 ) -> ::core::result::Result<(), ::edifact_rs::EdifactError> {
1255 #slot_prelude
1256 let mut __parts: ::std::vec::Vec<(
1257 usize,
1258 usize,
1259 ::std::borrow::Cow<'_, str>,
1260 )> = ::std::vec::Vec::with_capacity(#capacity);
1261 #(#stmts)*
1262 ::edifact_rs::emit_sparse_segment(emitter, #seg_tag, &mut __parts)
1263 }
1264 }
1265 }
1266}
1267
1268fn impl_serialize(input: &DeriveInput) -> syn::Result<TokenStream2> {
1269 let name = &input.ident;
1270 let struct_attrs = parse_struct_attrs(input)?;
1271 let fields = get_named_fields(input)?;
1272 let is_segment_struct = struct_attrs.segment.is_some();
1273
1274 let field_data: Vec<(&syn::Ident, &Type, FieldAttrs)> = fields
1276 .named
1277 .iter()
1278 .map(|f| {
1279 let attrs = parse_field_attrs(f)?;
1280 let ident = f
1281 .ident
1282 .as_ref()
1283 .ok_or_else(|| syn::Error::new_spanned(f, "only named fields are supported"))?;
1284 validate_field_attrs(ident, &f.ty, &attrs, is_segment_struct)?;
1285 Ok((ident, &f.ty, attrs))
1286 })
1287 .collect::<syn::Result<_>>()?;
1288 check_duplicate_slots(&field_data, is_segment_struct)?;
1289 let (slot_prelude, slots) = resolve_slots(&struct_attrs, &field_data)?;
1290 let uses_code_slots = field_data
1291 .iter()
1292 .any(|(_, _, attrs)| matches!(attrs.element, Some(Position::Code(_))));
1293
1294 let body = if let Some(seg_tag) = &struct_attrs.segment {
1295 if uses_code_slots {
1296 return Ok(impl_serialize_sparse(
1297 name,
1298 seg_tag,
1299 &struct_attrs,
1300 &field_data,
1301 &slots,
1302 &slot_prelude,
1303 ));
1304 }
1305 let mut grid: std::collections::BTreeMap<u32, std::collections::BTreeMap<u32, Cell>> =
1319 std::collections::BTreeMap::new();
1320
1321 if struct_attrs.qualifier.is_some() {
1322 for (i, (ident, _, attrs)) in field_data.iter().enumerate() {
1324 let elem = static_element_index(attrs, i);
1325 let comp = attrs.component.unwrap_or(0);
1326 if elem == 0 && comp == 0 {
1327 return Err(syn::Error::new(
1328 attrs
1329 .element_span
1330 .or(attrs.component_span)
1331 .unwrap_or_else(|| ident.span()),
1332 format!(
1333 "field `{ident}`: cannot use #[edifact(qualifier = ...)] with a field at element = 0 without component >= 1; the qualifier occupies component 0"
1334 ),
1335 ));
1336 }
1337 }
1338 grid.entry(0).or_default().insert(0, Cell::Qualifier);
1339 }
1340
1341 for (i, (_, _, attrs)) in field_data.iter().enumerate() {
1342 if attrs.group {
1345 continue;
1346 }
1347 let element = static_element_index(attrs, i);
1348 let component = attrs.component.unwrap_or(0);
1349 grid.entry(element)
1351 .or_default()
1352 .insert(component, Cell::Field(i));
1353 }
1354
1355 let empty_element = quote! {
1356 emitter.emit(::edifact_rs::EdifactEvent::element(""))?;
1357 };
1358 let empty_component = quote! {
1359 emitter.emit(::edifact_rs::EdifactEvent::component(""))?;
1360 };
1361
1362 let mut elem_stmts: Vec<TokenStream2> = Vec::new();
1363 if let Some(&max_element) = grid.keys().max() {
1364 for element in 0..=max_element {
1365 let Some(components) = grid.get(&element) else {
1366 elem_stmts.push(empty_element.clone());
1368 continue;
1369 };
1370 let max_component = components.keys().max().copied().unwrap_or(0);
1371
1372 let repeating: Vec<u32> = components
1376 .iter()
1377 .filter_map(|(component, cell)| match cell {
1378 Cell::Field(index) if field_data[*index].2.repeat => Some(*component),
1379 _ => None,
1380 })
1381 .collect();
1382 if !repeating.is_empty() {
1383 if let Some(&component) = components.iter().find_map(|(c, cell)| match cell {
1384 Cell::Field(index) if field_data[*index].2.composite => Some(c),
1385 _ => None,
1386 }) {
1387 let (ident, _, _) = &field_data[match components[&component] {
1388 Cell::Field(index) => index,
1389 Cell::Qualifier => unreachable!("a qualifier is not composite"),
1390 }];
1391 return Err(syn::Error::new(
1392 ident.span(),
1393 format!(
1394 "field `{ident}`: a data element cannot hold both a \
1395 #[edifact(composite)] field and a #[edifact(repeat)] one — the \
1396 composite already spans the whole element"
1397 ),
1398 ));
1399 }
1400 let cells: Vec<Option<(&syn::Ident, &Type, bool)>> = (0..=max_component)
1401 .map(|component| match components.get(&component) {
1402 Some(Cell::Field(index)) => {
1403 let (ident, ty, attrs) = &field_data[*index];
1404 Some((*ident, *ty, attrs.repeat))
1405 }
1406 Some(Cell::Qualifier) | None => None,
1410 })
1411 .collect();
1412 let mut cells = cells;
1413 if matches!(components.get(&0), Some(Cell::Qualifier)) {
1414 let qual = struct_attrs.qualifier.clone().unwrap_or_default();
1415 let literal = syn::LitStr::new(&qual, proc_macro2::Span::call_site());
1416 elem_stmts.push(emit_repeating_element_with_qualifier(
1417 &literal,
1418 &cells[1..],
1419 &repeat_lengths(&field_data, components),
1420 ));
1421 continue;
1422 }
1423 cells.truncate(usize::try_from(max_component).unwrap_or(0) + 1);
1424 elem_stmts.push(emit_repeating_element(
1425 &cells,
1426 &repeat_lengths(&field_data, components),
1427 ));
1428 continue;
1429 }
1430
1431 for component in 0..=max_component {
1432 match components.get(&component) {
1433 Some(Cell::Qualifier) => {
1434 let qual = struct_attrs.qualifier.as_deref().unwrap_or("");
1435 elem_stmts.push(quote! {
1436 emitter.emit(::edifact_rs::EdifactEvent::element(#qual))?;
1437 });
1438 }
1439 Some(Cell::Field(index)) => {
1440 let (ident, ty, attrs) = &field_data[*index];
1441 if attrs.composite {
1442 elem_stmts.push(emit_composite_field(ident, ty));
1445 } else if component == 0 {
1446 elem_stmts.push(emit_element(ident, ty));
1447 } else {
1448 elem_stmts.push(emit_component_element(ident, ty));
1449 }
1450 }
1451 None if component == 0 => elem_stmts.push(empty_element.clone()),
1452 None => elem_stmts.push(empty_component.clone()),
1453 }
1454 }
1455 }
1456 }
1457
1458 quote! {
1459 emitter.emit(::edifact_rs::EdifactEvent::start(#seg_tag))?;
1460 #(#elem_stmts)*
1461 emitter.emit(::edifact_rs::EdifactEvent::EndSegment)?;
1462 }
1463 } else {
1464 let stmts: Vec<TokenStream2> = field_data
1466 .iter()
1467 .map(|(ident, ty, attrs)| {
1468 if attrs.group || is_vec_type(ty) {
1469 quote! {
1470 for __item in &self.#ident {
1471 ::edifact_rs::EdifactSerialize::edifact_serialize(__item, emitter)?;
1472 }
1473 }
1474 } else {
1475 quote! {
1476 ::edifact_rs::EdifactSerialize::edifact_serialize(&self.#ident, emitter)?;
1477 }
1478 }
1479 })
1480 .collect();
1481 quote! { #(#stmts)* }
1482 };
1483
1484 Ok(quote! {
1485 impl ::edifact_rs::EdifactSerialize for #name {
1486 fn edifact_serialize<__E: ::edifact_rs::EventEmitter>(
1487 &self,
1488 emitter: &mut __E,
1489 ) -> ::core::result::Result<(), ::edifact_rs::EdifactError> {
1490 #body
1491 ::core::result::Result::Ok(())
1492 }
1493 }
1494 })
1495}
1496
1497fn repeating_field_init(
1505 ident: &syn::Ident,
1506 ty: &Type,
1507 element: &TokenStream2,
1508 component: &TokenStream2,
1509) -> syn::Result<TokenStream2> {
1510 let inner = vec_inner_type(ty).ok_or_else(|| {
1511 syn::Error::new(
1512 ident.span(),
1513 format!("field `{ident}`: #[edifact(repeat)] requires Vec<T>"),
1514 )
1515 })?;
1516 let convert = if is_string_type(inner) {
1519 quote! { ::core::result::Result::Ok(::std::string::ToString::to_string(__value)) }
1520 } else {
1521 let message = format!(
1522 "field `{ident}`: repeating value is not a valid {}",
1523 quote!(#inner)
1524 );
1525 quote! {
1526 __value.parse::<#inner>().map_err(|_| ::edifact_rs::EdifactError::InvalidFieldValue {
1527 tag: __seg.tag().to_string(),
1528 element_index: #element,
1529 value: ::std::format!("{}: {}", #message, __value),
1530 })
1531 }
1532 };
1533 Ok(quote! {
1534 let #ident = __seg.repeated_component(#element, #component)
1535 .map(|__value| #convert)
1536 .collect::<::core::result::Result<::std::vec::Vec<#inner>, ::edifact_rs::EdifactError>>()?;
1537 })
1538}
1539
1540enum Cell {
1542 Qualifier,
1544 Field(usize),
1546}
1547
1548fn repeat_lengths(
1550 field_data: &[(&syn::Ident, &Type, FieldAttrs)],
1551 components: &std::collections::BTreeMap<u32, Cell>,
1552) -> Vec<TokenStream2> {
1553 components
1554 .values()
1555 .filter_map(|cell| match cell {
1556 Cell::Field(index) if field_data[*index].2.repeat => {
1557 let ident = field_data[*index].0;
1558 Some(quote! { self.#ident.len() })
1559 }
1560 _ => None,
1561 })
1562 .collect()
1563}
1564
1565fn emit_repeating_element_with_qualifier(
1568 qualifier: &syn::LitStr,
1569 rest: &[Option<(&syn::Ident, &Type, bool)>],
1570 lengths: &[TokenStream2],
1571) -> TokenStream2 {
1572 let values: Vec<TokenStream2> = rest
1573 .iter()
1574 .map(|cell| match cell {
1575 Some((ident, ty, repeat)) => occurrence_value(ident, ty, *repeat),
1576 None => quote! { ::std::borrow::Cow::Borrowed("") },
1577 })
1578 .collect();
1579 quote! {
1580 {
1581 let __n = ::core::cmp::max(1usize, [#(#lengths),*].into_iter().max().unwrap_or(0));
1582 for __k in 0..__n {
1583 let __event = if __k == 0 {
1584 ::edifact_rs::EdifactEvent::element(#qualifier)
1585 } else {
1586 ::edifact_rs::EdifactEvent::repeat(#qualifier)
1587 };
1588 emitter.emit(__event)?;
1589 #(
1590 let __v = #values;
1591 emitter.emit(::edifact_rs::EdifactEvent::component(__v.as_ref()))?;
1592 )*
1593 }
1594 }
1595 }
1596}
1597
1598fn occurrence_value(ident: &syn::Ident, ty: &Type, repeat: bool) -> TokenStream2 {
1605 let empty = quote! { ::std::borrow::Cow::Borrowed("") };
1606 if repeat {
1607 let inner_is_str = vec_inner_type(ty).is_some_and(is_str_like);
1608 let map = if inner_is_str {
1609 quote! { |__v| ::std::borrow::Cow::Borrowed(__v.as_ref()) }
1610 } else {
1611 quote! { |__v| ::std::borrow::Cow::Owned(::std::string::ToString::to_string(__v)) }
1612 };
1613 return quote! { self.#ident.get(__k).map(#map).unwrap_or(#empty) };
1614 }
1615 if is_option_type(ty) {
1616 return if option_inner_type(ty).is_some_and(is_str_like) {
1617 quote! {
1618 self.#ident
1619 .as_deref()
1620 .map(::std::borrow::Cow::Borrowed)
1621 .unwrap_or(#empty)
1622 }
1623 } else {
1624 quote! {
1625 self.#ident
1626 .as_ref()
1627 .map(|__v| ::std::borrow::Cow::Owned(::std::string::ToString::to_string(__v)))
1628 .unwrap_or(#empty)
1629 }
1630 };
1631 }
1632 if is_string_type(ty) {
1633 quote! { ::std::borrow::Cow::Borrowed(self.#ident.as_str()) }
1634 } else if is_str_ref_type(ty) {
1635 quote! { ::std::borrow::Cow::Borrowed(self.#ident) }
1636 } else {
1637 quote! { ::std::borrow::Cow::Owned(::std::string::ToString::to_string(&self.#ident)) }
1638 }
1639}
1640
1641fn emit_repeating_element(
1652 components: &[Option<(&syn::Ident, &Type, bool)>],
1653 lengths: &[TokenStream2],
1654) -> TokenStream2 {
1655 let values: Vec<TokenStream2> = components
1656 .iter()
1657 .map(|cell| match cell {
1658 Some((ident, ty, repeat)) => occurrence_value(ident, ty, *repeat),
1659 None => quote! { ::std::borrow::Cow::Borrowed("") },
1660 })
1661 .collect();
1662 let (first, rest) = values.split_first().expect("an element has ≥1 component");
1663 quote! {
1664 {
1665 let __n = ::core::cmp::max(1usize, [#(#lengths),*].into_iter().max().unwrap_or(0));
1666 for __k in 0..__n {
1667 let __v = #first;
1668 let __event = if __k == 0 {
1669 ::edifact_rs::EdifactEvent::element(__v.as_ref())
1670 } else {
1671 ::edifact_rs::EdifactEvent::repeat(__v.as_ref())
1672 };
1673 emitter.emit(__event)?;
1674 #(
1675 let __v = #rest;
1676 emitter.emit(::edifact_rs::EdifactEvent::component(__v.as_ref()))?;
1677 )*
1678 }
1679 }
1680 }
1681}
1682
1683fn emit_element(ident: &syn::Ident, ty: &Type) -> TokenStream2 {
1688 if is_option_type(ty) {
1689 let inner_is_str = option_inner_type(ty).is_some_and(is_str_like);
1690 if inner_is_str {
1691 quote! {
1692 match &self.#ident {
1693 ::core::option::Option::Some(__v) => {
1694 emitter.emit(::edifact_rs::EdifactEvent::element(__v.as_str()))?;
1695 }
1696 ::core::option::Option::None => {
1697 emitter.emit(::edifact_rs::EdifactEvent::element(""))?;
1698 }
1699 }
1700 }
1701 } else {
1702 quote! {
1703 match &self.#ident {
1704 ::core::option::Option::Some(__v) => {
1705 let __s = ::std::string::ToString::to_string(__v);
1706 emitter.emit(::edifact_rs::EdifactEvent::element(&__s))?;
1707 }
1708 ::core::option::Option::None => {
1709 emitter.emit(::edifact_rs::EdifactEvent::element(""))?;
1710 }
1711 }
1712 }
1713 }
1714 } else if is_string_type(ty) {
1715 quote! {
1716 emitter.emit(::edifact_rs::EdifactEvent::element(self.#ident.as_str()))?;
1717 }
1718 } else if is_str_ref_type(ty) {
1719 quote! {
1720 emitter.emit(::edifact_rs::EdifactEvent::element(self.#ident))?;
1721 }
1722 } else {
1723 quote! {
1724 {
1725 let __s = ::std::string::ToString::to_string(&self.#ident);
1726 emitter.emit(::edifact_rs::EdifactEvent::element(&__s))?;
1727 }
1728 }
1729 }
1730}
1731
1732fn emit_component_element(ident: &syn::Ident, ty: &Type) -> TokenStream2 {
1736 if is_option_type(ty) {
1737 let inner_is_str = option_inner_type(ty).is_some_and(is_str_like);
1738 if inner_is_str {
1739 quote! {
1740 match &self.#ident {
1741 ::core::option::Option::Some(__v) => {
1742 emitter.emit(::edifact_rs::EdifactEvent::component(__v.as_str()))?;
1743 }
1744 ::core::option::Option::None => {
1745 emitter.emit(::edifact_rs::EdifactEvent::component(""))?;
1746 }
1747 }
1748 }
1749 } else {
1750 quote! {
1751 match &self.#ident {
1752 ::core::option::Option::Some(__v) => {
1753 let __s = ::std::string::ToString::to_string(__v);
1754 emitter.emit(::edifact_rs::EdifactEvent::component(&__s))?;
1755 }
1756 ::core::option::Option::None => {
1757 emitter.emit(::edifact_rs::EdifactEvent::component(""))?;
1758 }
1759 }
1760 }
1761 }
1762 } else if is_string_type(ty) {
1763 quote! {
1764 emitter.emit(::edifact_rs::EdifactEvent::component(self.#ident.as_str()))?;
1765 }
1766 } else if is_str_ref_type(ty) {
1767 quote! {
1768 emitter.emit(::edifact_rs::EdifactEvent::component(self.#ident))?;
1769 }
1770 } else {
1771 quote! {
1772 {
1773 let __s = ::std::string::ToString::to_string(&self.#ident);
1774 emitter.emit(::edifact_rs::EdifactEvent::component(&__s))?;
1775 }
1776 }
1777 }
1778}
1779
1780fn emit_composite_field(ident: &syn::Ident, ty: &Type) -> TokenStream2 {
1782 if is_option_type(ty) {
1783 quote! {
1784 match &self.#ident {
1785 ::core::option::Option::Some(__v) => {
1786 ::edifact_rs::EdifactCompositeSerialize::edifact_serialize_composite(__v, emitter)?;
1787 }
1788 ::core::option::Option::None => {
1789 emitter.emit(::edifact_rs::EdifactEvent::element(""))?;
1790 }
1791 }
1792 }
1793 } else {
1794 quote! {
1795 ::edifact_rs::EdifactCompositeSerialize::edifact_serialize_composite(&self.#ident, emitter)?;
1796 }
1797 }
1798}
1799
1800fn impl_deserialize(input: &DeriveInput) -> syn::Result<TokenStream2> {
1803 let name = &input.ident;
1804 let struct_attrs = parse_struct_attrs(input)?;
1805 let fields = get_named_fields(input)?;
1806 let is_segment_struct = struct_attrs.segment.is_some();
1807
1808 let field_data: Vec<(&syn::Ident, &Type, FieldAttrs)> = fields
1809 .named
1810 .iter()
1811 .map(|f| {
1812 let attrs = parse_field_attrs(f)?;
1813 let ident = f
1814 .ident
1815 .as_ref()
1816 .ok_or_else(|| syn::Error::new_spanned(f, "only named fields are supported"))?;
1817 validate_field_attrs(ident, &f.ty, &attrs, is_segment_struct)?;
1818 Ok((ident, &f.ty, attrs))
1819 })
1820 .collect::<syn::Result<_>>()?;
1821 check_duplicate_slots(&field_data, is_segment_struct)?;
1822 let (slot_prelude, slots) = resolve_slots(&struct_attrs, &field_data)?;
1823
1824 let field_names: Vec<&syn::Ident> = field_data.iter().map(|(id, _, _)| *id).collect();
1825
1826 let (body, segment_tag_impl) = if let Some(seg_tag) = &struct_attrs.segment {
1827 let qualifier_guard = if let Some(qual) = &struct_attrs.qualifier {
1829 quote! {
1830 if __seg.element_str(0).unwrap_or("") != #qual {
1831 return ::core::result::Result::Err(
1832 ::edifact_rs::EdifactError::MissingRequiredElement {
1833 tag: #seg_tag.to_owned(),
1834 element_index: 0,
1835 }
1836 );
1837 }
1838 }
1839 } else if let Some(idx) = struct_attrs.qualifier_from {
1840 quote! {
1841 match __seg.element_str(#idx as usize) {
1845 ::core::option::Option::None => return ::core::result::Result::Err(
1846 ::edifact_rs::EdifactError::MissingRequiredElement {
1847 tag: #seg_tag.to_owned(),
1848 element_index: #idx as usize,
1849 }
1850 ),
1851 ::core::option::Option::Some("") => return ::core::result::Result::Err(
1852 ::edifact_rs::EdifactError::InvalidFieldValue {
1853 tag: #seg_tag.to_owned(),
1854 element_index: #idx as usize,
1855 value: ::std::string::String::new(),
1856 }
1857 ),
1858 ::core::option::Option::Some(__qual_val) => { let _ = __qual_val; }
1859 }
1860 }
1861 } else {
1862 quote! {}
1863 };
1864
1865 let find_seg = if let Some(qual) = &struct_attrs.qualifier {
1866 quote! {
1867 ::edifact_rs::find_qualified_segment(segments, #seg_tag, #qual)
1868 }
1869 } else {
1870 quote! {
1871 ::edifact_rs::find_segment(segments, #seg_tag)
1872 }
1873 };
1874
1875 let field_inits: Vec<TokenStream2> = field_data
1876 .iter()
1877 .zip(slots.iter())
1878 .map(|((ident, ty, attrs), slot)| -> syn::Result<TokenStream2> {
1879 let idx = &slot.element;
1880 if attrs.repeat {
1881 return repeating_field_init(ident, ty, &slot.element, &slot.component);
1882 }
1883 if attrs.composite {
1884 if is_option_type(ty) {
1885 let inner_ty = option_inner_type(ty)
1886 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1887 return Ok(quote! {
1888 let #ident = match ::edifact_rs::composite_element(__seg, #idx) {
1889 ::core::option::Option::Some(__composite) => {
1890 ::core::option::Option::Some(
1891 <#inner_ty as ::edifact_rs::EdifactCompositeDeserialize>::edifact_deserialize_composite(__composite)?
1892 )
1893 }
1894 ::core::option::Option::None => ::core::option::Option::None,
1895 };
1896 });
1897 }
1898 return Ok(quote! {
1899 let #ident = <#ty as ::edifact_rs::EdifactCompositeDeserialize>::edifact_deserialize_composite(
1900 ::edifact_rs::composite_element(__seg, #idx).ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
1901 tag: #seg_tag.to_owned(),
1902 element_index: #idx,
1903 })?
1904 )?;
1905 });
1906 }
1907 let comp = &slot.component;
1908 let value_expr = if slot.has_component {
1909 quote! {
1910 __seg.get_element(#idx).and_then(|__e| __e.get_component(#comp))
1911 }
1912 } else {
1913 quote! { __seg.element_str(#idx) }
1914 };
1915 let names_component = &slot.names_component;
1919 let missing_required_err = quote! {
1920 if #names_component {
1921 ::edifact_rs::EdifactError::MissingRequiredComponent {
1922 tag: #seg_tag.to_owned(),
1923 element_index: #idx,
1924 component_index: #comp,
1925 }
1926 } else {
1927 ::edifact_rs::EdifactError::MissingRequiredElement {
1928 tag: #seg_tag.to_owned(),
1929 element_index: #idx,
1930 }
1931 }
1932 };
1933 Ok(if is_option_type(ty) {
1934 let inner_ty = option_inner_type(ty);
1935 let inner_is_str = inner_ty.is_some_and(is_str_like);
1936 if attrs.required {
1937 if inner_is_str {
1941 quote! {
1942 let #ident = ::core::option::Option::Some(
1943 #value_expr
1944 .filter(|__s| !__s.is_empty())
1945 .ok_or_else(|| #missing_required_err)?
1946 .to_owned()
1947 );
1948 }
1949 } else {
1950 let inner_ty = inner_ty
1951 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1952 quote! {
1953 let #ident = ::core::option::Option::Some(
1954 #value_expr
1955 .filter(|__s| !__s.is_empty())
1956 .ok_or_else(|| #missing_required_err)?
1957 .parse::<#inner_ty>()
1958 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })?
1959 );
1960 }
1961 }
1962 } else if inner_is_str {
1963 quote! {
1964 let #ident = #value_expr
1965 .filter(|__s| !__s.is_empty())
1966 .map(::std::string::String::from);
1967 }
1968 } else {
1969 let inner_ty = inner_ty
1970 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1971 quote! {
1972 let #ident = #value_expr
1973 .filter(|__s| !__s.is_empty())
1974 .map(|__s| __s.parse::<#inner_ty>()
1975 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })
1976 )
1977 .transpose()?;
1978 }
1979 }
1980 } else if is_str_like(ty) {
1981 quote! {
1982 let #ident = #value_expr
1983 .filter(|__s| !__s.is_empty())
1984 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
1985 tag: #seg_tag.to_owned(),
1986 element_index: #idx,
1987 })?
1988 .to_owned();
1989 }
1990 } else {
1991 quote! {
1992 let #ident = #value_expr
1993 .filter(|__s| !__s.is_empty())
1994 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
1995 tag: #seg_tag.to_owned(),
1996 element_index: #idx,
1997 })?
1998 .parse::<#ty>()
1999 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })?;
2000 }
2001 })
2002 })
2003 .collect::<syn::Result<_>>()?;
2004
2005 let body = quote! {
2006 #slot_prelude
2007 let __seg = #find_seg
2008 .ok_or_else(|| ::edifact_rs::EdifactError::MissingSegment {
2009 tag: #seg_tag.to_owned(),
2010 expected_position: "message body".to_owned(),
2011 })?;
2012 #qualifier_guard
2013 #(#field_inits)*
2014 ::core::result::Result::Ok(Self { #(#field_names),* })
2015 };
2016
2017 let qualifier_match = if let Some(qual) = &struct_attrs.qualifier {
2023 quote! {
2024 const QUALIFIER_PATTERN: ::core::option::Option<&'static str> =
2025 ::core::option::Option::Some(#qual);
2026 }
2027 } else if let Some(idx) = struct_attrs.qualifier_from {
2028 quote! {
2032 fn matches_segment(seg: &::edifact_rs::Segment<'_>) -> bool {
2033 seg.tag == Self::SEGMENT_TAG
2034 && !seg.element_str(#idx as usize).unwrap_or("").is_empty()
2035 }
2036 }
2037 } else {
2038 quote! {}
2039 };
2040
2041 let seg_tag_impl = quote! {
2042 impl ::edifact_rs::EdifactSegmentTag for #name {
2043 const SEGMENT_TAG: &'static str = #seg_tag;
2044 #qualifier_match
2045 }
2046 };
2047
2048 (body, seg_tag_impl)
2049 } else {
2050 let field_inits: Vec<TokenStream2> = field_data
2052 .iter()
2053 .map(|(ident, ty, attrs)| -> syn::Result<TokenStream2> {
2054 Ok(if let Some(qual) = &attrs.qualifier {
2055 if attrs.group || is_vec_type(ty) {
2056 let inner_ty = vec_inner_type(ty)
2057 .ok_or_else(|| syn::Error::new(ident.span(), "expected Vec<T>"))?;
2058 quote! {
2059 let #ident = segments
2060 .iter()
2061 .filter(|__seg| {
2062 __seg.tag == <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG
2063 && __seg.element_str(0).unwrap_or("") == #qual
2064 })
2065 .map(|__seg| {
2066 ::edifact_rs::EdifactDeserialize::edifact_deserialize(
2067 ::core::slice::from_ref(__seg),
2068 )
2069 })
2070 .collect::<::core::result::Result<::std::vec::Vec<#inner_ty>, ::edifact_rs::EdifactError>>()?;
2071 }
2072 } else if is_option_type(ty) {
2073 let inner_ty = option_inner_type(ty)
2074 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
2075 quote! {
2076 let #ident = match ::edifact_rs::find_qualified_segment(
2077 segments,
2078 <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG,
2079 #qual,
2080 ) {
2081 ::core::option::Option::Some(__seg) => {
2082 ::core::option::Option::Some(
2083 ::edifact_rs::EdifactDeserialize::edifact_deserialize(
2084 ::core::slice::from_ref(__seg),
2085 )?
2086 )
2087 }
2088 ::core::option::Option::None => ::core::option::Option::None,
2089 };
2090 }
2091 } else {
2092 quote! {
2093 let __seg = ::edifact_rs::find_qualified_segment(
2094 segments,
2095 <#ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG,
2096 #qual,
2097 )
2098 .ok_or_else(|| ::edifact_rs::EdifactError::MissingSegment {
2099 tag: <#ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG.to_owned(),
2100 expected_position: "message body".to_owned(),
2101 })?;
2102 let #ident = ::edifact_rs::EdifactDeserialize::edifact_deserialize(
2103 ::core::slice::from_ref(__seg),
2104 )?;
2105 }
2106 }
2107 } else if attrs.group || is_vec_type(ty) {
2108 let inner_ty = vec_inner_type(ty)
2109 .ok_or_else(|| syn::Error::new(ident.span(), "expected Vec<T>"))?;
2110 quote! {
2111 let #ident = ::edifact_rs::find_segments_typed::<#inner_ty>(segments)
2112 .map(|__seg| {
2113 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize(
2114 ::core::slice::from_ref(__seg),
2115 )
2116 })
2117 .collect::<::core::result::Result<::std::vec::Vec<#inner_ty>, _>>()?;
2118 }
2119 } else if is_option_type(ty) {
2120 let inner_ty = option_inner_type(ty)
2121 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
2122 quote! {
2123 let #ident = if segments
2124 .iter()
2125 .any(|__seg| __seg.tag == <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG)
2126 {
2127 ::core::option::Option::Some(
2128 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize(segments)?
2129 )
2130 } else {
2131 ::core::option::Option::None
2132 };
2133 }
2134 } else {
2135 quote! {
2136 let #ident = ::edifact_rs::EdifactDeserialize::edifact_deserialize(segments)?;
2137 }
2138 })
2139 })
2140 .collect::<syn::Result<_>>()?;
2141
2142 let body = quote! {
2143 #(#field_inits)*
2144 ::core::result::Result::Ok(Self { #(#field_names),* })
2145 };
2146
2147 (body, quote! {})
2148 };
2149
2150 Ok(quote! {
2151 impl ::edifact_rs::EdifactDeserialize for #name {
2152 fn edifact_deserialize(
2153 segments: &[::edifact_rs::Segment<'_>],
2154 ) -> ::core::result::Result<Self, ::edifact_rs::EdifactError> {
2155 #body
2156 }
2157 }
2158 #segment_tag_impl
2159 })
2160}
2161
2162#[cfg(test)]
2163mod tests {
2164 #[test]
2178 fn trybuild_ui() {
2179 if std::env::var_os("EDIFACT_UI_TESTS").is_none() {
2180 eprintln!(
2181 "skipping derive UI suite: set EDIFACT_UI_TESTS=1 to run it \
2182 (expectations are pinned to the MSRV toolchain)"
2183 );
2184 return;
2185 }
2186 let t = trybuild::TestCases::new();
2187 t.pass("tests/ui/pass_*.rs");
2188 t.compile_fail("tests/ui/fail_*.rs");
2189 }
2190}