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: #value })?; }
937 } else {
938 quote! {
939 __emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: #value })?;
940 }
941 }
942 });
943
944 let body = if slots.is_empty() {
946 quote! { __emitter.emit(::edifact_rs::EdifactEvent::Element { value: "" })?; }
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::OwnedEdifactEvent::Element { value } => {
1195 __comp = 0;
1196 __any = true;
1197 __parts.push((#element, 0usize, ::std::borrow::Cow::Owned(value)));
1198 }
1199 ::edifact_rs::OwnedEdifactEvent::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 { value: "" })?;
1357 };
1358 let empty_component = quote! {
1359 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: "" })?;
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 { value: #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::StartSegment { tag: #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 owned: bool,
1510) -> syn::Result<TokenStream2> {
1511 let inner = vec_inner_type(ty).ok_or_else(|| {
1512 syn::Error::new(
1513 ident.span(),
1514 format!("field `{ident}`: #[edifact(repeat)] requires Vec<T>"),
1515 )
1516 })?;
1517 let reader = if owned {
1518 quote! { ::edifact_rs::repeated_components_owned }
1519 } else {
1520 quote! { ::edifact_rs::repeated_components }
1521 };
1522 let convert = if is_string_type(inner) {
1526 quote! { ::core::result::Result::Ok(::std::string::ToString::to_string(__value)) }
1527 } else {
1528 let message = format!(
1529 "field `{ident}`: repeating value is not a valid {}",
1530 quote!(#inner)
1531 );
1532 quote! {
1533 __value.parse::<#inner>().map_err(|_| ::edifact_rs::EdifactError::InvalidFieldValue {
1534 tag: __seg.tag.to_string(),
1535 element_index: #element,
1536 value: ::std::format!("{}: {}", #message, __value),
1537 })
1538 }
1539 };
1540 Ok(quote! {
1541 let #ident = #reader(__seg, #element, #component)
1542 .map(|__value| #convert)
1543 .collect::<::core::result::Result<::std::vec::Vec<#inner>, ::edifact_rs::EdifactError>>()?;
1544 })
1545}
1546
1547enum Cell {
1549 Qualifier,
1551 Field(usize),
1553}
1554
1555fn repeat_lengths(
1557 field_data: &[(&syn::Ident, &Type, FieldAttrs)],
1558 components: &std::collections::BTreeMap<u32, Cell>,
1559) -> Vec<TokenStream2> {
1560 components
1561 .values()
1562 .filter_map(|cell| match cell {
1563 Cell::Field(index) if field_data[*index].2.repeat => {
1564 let ident = field_data[*index].0;
1565 Some(quote! { self.#ident.len() })
1566 }
1567 _ => None,
1568 })
1569 .collect()
1570}
1571
1572fn emit_repeating_element_with_qualifier(
1575 qualifier: &syn::LitStr,
1576 rest: &[Option<(&syn::Ident, &Type, bool)>],
1577 lengths: &[TokenStream2],
1578) -> TokenStream2 {
1579 let values: Vec<TokenStream2> = rest
1580 .iter()
1581 .map(|cell| match cell {
1582 Some((ident, ty, repeat)) => occurrence_value(ident, ty, *repeat),
1583 None => quote! { ::std::borrow::Cow::Borrowed("") },
1584 })
1585 .collect();
1586 quote! {
1587 {
1588 let __n = ::core::cmp::max(1usize, [#(#lengths),*].into_iter().max().unwrap_or(0));
1589 for __k in 0..__n {
1590 let __event = if __k == 0 {
1591 ::edifact_rs::EdifactEvent::Element { value: #qualifier }
1592 } else {
1593 ::edifact_rs::EdifactEvent::RepeatElement { value: #qualifier }
1594 };
1595 emitter.emit(__event)?;
1596 #(
1597 let __v = #values;
1598 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement {
1599 value: __v.as_ref(),
1600 })?;
1601 )*
1602 }
1603 }
1604 }
1605}
1606
1607fn occurrence_value(ident: &syn::Ident, ty: &Type, repeat: bool) -> TokenStream2 {
1614 let empty = quote! { ::std::borrow::Cow::Borrowed("") };
1615 if repeat {
1616 let inner_is_str = vec_inner_type(ty).is_some_and(is_str_like);
1617 let map = if inner_is_str {
1618 quote! { |__v| ::std::borrow::Cow::Borrowed(__v.as_ref()) }
1619 } else {
1620 quote! { |__v| ::std::borrow::Cow::Owned(::std::string::ToString::to_string(__v)) }
1621 };
1622 return quote! { self.#ident.get(__k).map(#map).unwrap_or(#empty) };
1623 }
1624 if is_option_type(ty) {
1625 return if option_inner_type(ty).is_some_and(is_str_like) {
1626 quote! {
1627 self.#ident
1628 .as_deref()
1629 .map(::std::borrow::Cow::Borrowed)
1630 .unwrap_or(#empty)
1631 }
1632 } else {
1633 quote! {
1634 self.#ident
1635 .as_ref()
1636 .map(|__v| ::std::borrow::Cow::Owned(::std::string::ToString::to_string(__v)))
1637 .unwrap_or(#empty)
1638 }
1639 };
1640 }
1641 if is_string_type(ty) {
1642 quote! { ::std::borrow::Cow::Borrowed(self.#ident.as_str()) }
1643 } else if is_str_ref_type(ty) {
1644 quote! { ::std::borrow::Cow::Borrowed(self.#ident) }
1645 } else {
1646 quote! { ::std::borrow::Cow::Owned(::std::string::ToString::to_string(&self.#ident)) }
1647 }
1648}
1649
1650fn emit_repeating_element(
1661 components: &[Option<(&syn::Ident, &Type, bool)>],
1662 lengths: &[TokenStream2],
1663) -> TokenStream2 {
1664 let values: Vec<TokenStream2> = components
1665 .iter()
1666 .map(|cell| match cell {
1667 Some((ident, ty, repeat)) => occurrence_value(ident, ty, *repeat),
1668 None => quote! { ::std::borrow::Cow::Borrowed("") },
1669 })
1670 .collect();
1671 let (first, rest) = values.split_first().expect("an element has ≥1 component");
1672 quote! {
1673 {
1674 let __n = ::core::cmp::max(1usize, [#(#lengths),*].into_iter().max().unwrap_or(0));
1675 for __k in 0..__n {
1676 let __v = #first;
1677 let __event = if __k == 0 {
1678 ::edifact_rs::EdifactEvent::Element { value: __v.as_ref() }
1679 } else {
1680 ::edifact_rs::EdifactEvent::RepeatElement { value: __v.as_ref() }
1681 };
1682 emitter.emit(__event)?;
1683 #(
1684 let __v = #rest;
1685 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement {
1686 value: __v.as_ref(),
1687 })?;
1688 )*
1689 }
1690 }
1691 }
1692}
1693
1694fn emit_element(ident: &syn::Ident, ty: &Type) -> TokenStream2 {
1699 if is_option_type(ty) {
1700 let inner_is_str = option_inner_type(ty).is_some_and(is_str_like);
1701 if inner_is_str {
1702 quote! {
1703 match &self.#ident {
1704 ::core::option::Option::Some(__v) => {
1705 emitter.emit(::edifact_rs::EdifactEvent::Element { value: __v.as_str() })?;
1706 }
1707 ::core::option::Option::None => {
1708 emitter.emit(::edifact_rs::EdifactEvent::Element { value: "" })?;
1709 }
1710 }
1711 }
1712 } else {
1713 quote! {
1714 match &self.#ident {
1715 ::core::option::Option::Some(__v) => {
1716 let __s = ::std::string::ToString::to_string(__v);
1717 emitter.emit(::edifact_rs::EdifactEvent::Element { value: &__s })?;
1718 }
1719 ::core::option::Option::None => {
1720 emitter.emit(::edifact_rs::EdifactEvent::Element { value: "" })?;
1721 }
1722 }
1723 }
1724 }
1725 } else if is_string_type(ty) {
1726 quote! {
1727 emitter.emit(::edifact_rs::EdifactEvent::Element { value: self.#ident.as_str() })?;
1728 }
1729 } else if is_str_ref_type(ty) {
1730 quote! {
1731 emitter.emit(::edifact_rs::EdifactEvent::Element { value: self.#ident })?;
1732 }
1733 } else {
1734 quote! {
1735 {
1736 let __s = ::std::string::ToString::to_string(&self.#ident);
1737 emitter.emit(::edifact_rs::EdifactEvent::Element { value: &__s })?;
1738 }
1739 }
1740 }
1741}
1742
1743fn emit_component_element(ident: &syn::Ident, ty: &Type) -> TokenStream2 {
1747 if is_option_type(ty) {
1748 let inner_is_str = option_inner_type(ty).is_some_and(is_str_like);
1749 if inner_is_str {
1750 quote! {
1751 match &self.#ident {
1752 ::core::option::Option::Some(__v) => {
1753 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: __v.as_str() })?;
1754 }
1755 ::core::option::Option::None => {
1756 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: "" })?;
1757 }
1758 }
1759 }
1760 } else {
1761 quote! {
1762 match &self.#ident {
1763 ::core::option::Option::Some(__v) => {
1764 let __s = ::std::string::ToString::to_string(__v);
1765 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: &__s })?;
1766 }
1767 ::core::option::Option::None => {
1768 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: "" })?;
1769 }
1770 }
1771 }
1772 }
1773 } else if is_string_type(ty) {
1774 quote! {
1775 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: self.#ident.as_str() })?;
1776 }
1777 } else if is_str_ref_type(ty) {
1778 quote! {
1779 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: self.#ident })?;
1780 }
1781 } else {
1782 quote! {
1783 {
1784 let __s = ::std::string::ToString::to_string(&self.#ident);
1785 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: &__s })?;
1786 }
1787 }
1788 }
1789}
1790
1791fn emit_composite_field(ident: &syn::Ident, ty: &Type) -> TokenStream2 {
1793 if is_option_type(ty) {
1794 quote! {
1795 match &self.#ident {
1796 ::core::option::Option::Some(__v) => {
1797 ::edifact_rs::EdifactCompositeSerialize::edifact_serialize_composite(__v, emitter)?;
1798 }
1799 ::core::option::Option::None => {
1800 emitter.emit(::edifact_rs::EdifactEvent::Element { value: "" })?;
1801 }
1802 }
1803 }
1804 } else {
1805 quote! {
1806 ::edifact_rs::EdifactCompositeSerialize::edifact_serialize_composite(&self.#ident, emitter)?;
1807 }
1808 }
1809}
1810
1811fn impl_deserialize(input: &DeriveInput) -> syn::Result<TokenStream2> {
1814 let name = &input.ident;
1815 let struct_attrs = parse_struct_attrs(input)?;
1816 let fields = get_named_fields(input)?;
1817 let is_segment_struct = struct_attrs.segment.is_some();
1818
1819 let field_data: Vec<(&syn::Ident, &Type, FieldAttrs)> = fields
1820 .named
1821 .iter()
1822 .map(|f| {
1823 let attrs = parse_field_attrs(f)?;
1824 let ident = f
1825 .ident
1826 .as_ref()
1827 .ok_or_else(|| syn::Error::new_spanned(f, "only named fields are supported"))?;
1828 validate_field_attrs(ident, &f.ty, &attrs, is_segment_struct)?;
1829 Ok((ident, &f.ty, attrs))
1830 })
1831 .collect::<syn::Result<_>>()?;
1832 check_duplicate_slots(&field_data, is_segment_struct)?;
1833 let (slot_prelude, slots) = resolve_slots(&struct_attrs, &field_data)?;
1834
1835 let field_names: Vec<&syn::Ident> = field_data.iter().map(|(id, _, _)| *id).collect();
1836
1837 let (body, owned_body, segment_tag_impl) = if let Some(seg_tag) = &struct_attrs.segment {
1838 let qualifier_guard = if let Some(qual) = &struct_attrs.qualifier {
1840 quote! {
1841 if __seg.element_str(0).unwrap_or("") != #qual {
1842 return ::core::result::Result::Err(
1843 ::edifact_rs::EdifactError::MissingRequiredElement {
1844 tag: #seg_tag.to_owned(),
1845 element_index: 0,
1846 }
1847 );
1848 }
1849 }
1850 } else if let Some(idx) = struct_attrs.qualifier_from {
1851 quote! {
1852 match __seg.element_str(#idx as usize) {
1856 ::core::option::Option::None => return ::core::result::Result::Err(
1857 ::edifact_rs::EdifactError::MissingRequiredElement {
1858 tag: #seg_tag.to_owned(),
1859 element_index: #idx as usize,
1860 }
1861 ),
1862 ::core::option::Option::Some("") => return ::core::result::Result::Err(
1863 ::edifact_rs::EdifactError::InvalidFieldValue {
1864 tag: #seg_tag.to_owned(),
1865 element_index: #idx as usize,
1866 value: ::std::string::String::new(),
1867 }
1868 ),
1869 ::core::option::Option::Some(__qual_val) => { let _ = __qual_val; }
1870 }
1871 }
1872 } else {
1873 quote! {}
1874 };
1875
1876 let find_seg = if let Some(qual) = &struct_attrs.qualifier {
1877 quote! {
1878 ::edifact_rs::find_qualified_segment(segments, #seg_tag, #qual)
1879 }
1880 } else {
1881 quote! {
1882 ::edifact_rs::find_segment(segments, #seg_tag)
1883 }
1884 };
1885
1886 let field_inits: Vec<TokenStream2> = field_data
1887 .iter()
1888 .zip(slots.iter())
1889 .map(|((ident, ty, attrs), slot)| -> syn::Result<TokenStream2> {
1890 let idx = &slot.element;
1891 if attrs.repeat {
1892 return repeating_field_init(ident, ty, &slot.element, &slot.component, false);
1893 }
1894 if attrs.composite {
1895 if is_option_type(ty) {
1896 let inner_ty = option_inner_type(ty)
1897 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1898 return Ok(quote! {
1899 let #ident = match ::edifact_rs::composite_element(__seg, #idx) {
1900 ::core::option::Option::Some(__composite) => {
1901 ::core::option::Option::Some(
1902 <#inner_ty as ::edifact_rs::EdifactCompositeDeserialize>::edifact_deserialize_composite(__composite)?
1903 )
1904 }
1905 ::core::option::Option::None => ::core::option::Option::None,
1906 };
1907 });
1908 }
1909 return Ok(quote! {
1910 let #ident = <#ty as ::edifact_rs::EdifactCompositeDeserialize>::edifact_deserialize_composite(
1911 ::edifact_rs::composite_element(__seg, #idx).ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
1912 tag: #seg_tag.to_owned(),
1913 element_index: #idx,
1914 })?
1915 )?;
1916 });
1917 }
1918 let comp = &slot.component;
1919 let value_expr = if slot.has_component {
1920 quote! {
1921 __seg.get_element(#idx).and_then(|__e| __e.get_component(#comp))
1922 }
1923 } else {
1924 quote! { __seg.element_str(#idx) }
1925 };
1926 let names_component = &slot.names_component;
1930 let missing_required_err = quote! {
1931 if #names_component {
1932 ::edifact_rs::EdifactError::MissingRequiredComponent {
1933 tag: #seg_tag.to_owned(),
1934 element_index: #idx,
1935 component_index: #comp,
1936 }
1937 } else {
1938 ::edifact_rs::EdifactError::MissingRequiredElement {
1939 tag: #seg_tag.to_owned(),
1940 element_index: #idx,
1941 }
1942 }
1943 };
1944 Ok(if is_option_type(ty) {
1945 let inner_ty = option_inner_type(ty);
1946 let inner_is_str = inner_ty.is_some_and(is_str_like);
1947 if attrs.required {
1948 if inner_is_str {
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 .to_owned()
1958 );
1959 }
1960 } else {
1961 let inner_ty = inner_ty
1962 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1963 quote! {
1964 let #ident = ::core::option::Option::Some(
1965 #value_expr
1966 .filter(|__s| !__s.is_empty())
1967 .ok_or_else(|| #missing_required_err)?
1968 .parse::<#inner_ty>()
1969 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })?
1970 );
1971 }
1972 }
1973 } else if inner_is_str {
1974 quote! {
1975 let #ident = #value_expr
1976 .filter(|__s| !__s.is_empty())
1977 .map(::std::string::String::from);
1978 }
1979 } else {
1980 let inner_ty = inner_ty
1981 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1982 quote! {
1983 let #ident = #value_expr
1984 .filter(|__s| !__s.is_empty())
1985 .map(|__s| __s.parse::<#inner_ty>()
1986 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })
1987 )
1988 .transpose()?;
1989 }
1990 }
1991 } else if is_str_like(ty) {
1992 quote! {
1993 let #ident = #value_expr
1994 .filter(|__s| !__s.is_empty())
1995 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
1996 tag: #seg_tag.to_owned(),
1997 element_index: #idx,
1998 })?
1999 .to_owned();
2000 }
2001 } else {
2002 quote! {
2003 let #ident = #value_expr
2004 .filter(|__s| !__s.is_empty())
2005 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
2006 tag: #seg_tag.to_owned(),
2007 element_index: #idx,
2008 })?
2009 .parse::<#ty>()
2010 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })?;
2011 }
2012 })
2013 })
2014 .collect::<syn::Result<_>>()?;
2015
2016 let body = quote! {
2017 #slot_prelude
2018 let __seg = #find_seg
2019 .ok_or_else(|| ::edifact_rs::EdifactError::MissingSegment {
2020 tag: #seg_tag.to_owned(),
2021 expected_position: "message body".to_owned(),
2022 })?;
2023 #qualifier_guard
2024 #(#field_inits)*
2025 ::core::result::Result::Ok(Self { #(#field_names),* })
2026 };
2027
2028 let qualifier_match = if let Some(qual) = &struct_attrs.qualifier {
2035 quote! {
2036 const QUALIFIER_PATTERN: ::core::option::Option<&'static str> =
2037 ::core::option::Option::Some(#qual);
2038 }
2039 } else if let Some(idx) = struct_attrs.qualifier_from {
2040 quote! {
2044 fn matches_segment(seg: &::edifact_rs::Segment<'_>) -> bool {
2045 seg.tag == Self::SEGMENT_TAG
2046 && !seg.element_str(#idx as usize).unwrap_or("").is_empty()
2047 }
2048
2049 fn matches_owned_segment(seg: &::edifact_rs::OwnedSegment) -> bool {
2050 seg.tag == Self::SEGMENT_TAG
2051 && !seg
2052 .elements
2053 .get(#idx as usize)
2054 .and_then(|e| e.components.first())
2055 .map(|(c, _)| c.as_str())
2056 .unwrap_or("")
2057 .is_empty()
2058 }
2059 }
2060 } else {
2061 quote! {}
2062 };
2063
2064 let seg_tag_impl = quote! {
2065 impl ::edifact_rs::EdifactSegmentTag for #name {
2066 const SEGMENT_TAG: &'static str = #seg_tag;
2067 #qualifier_match
2068 }
2069 };
2070
2071 let find_seg_owned = if let Some(qual) = &struct_attrs.qualifier {
2074 quote! {
2075 ::edifact_rs::find_qualified_segment_owned(segments, #seg_tag, #qual)
2076 }
2077 } else {
2078 quote! {
2079 ::edifact_rs::find_segment_owned(segments, #seg_tag)
2080 }
2081 };
2082
2083 let field_inits_owned: Vec<TokenStream2> = field_data
2084 .iter()
2085 .zip(slots.iter())
2086 .map(|((ident, ty, attrs), slot)| -> syn::Result<TokenStream2> {
2087 let idx = &slot.element;
2088 if attrs.repeat {
2089 return repeating_field_init(ident, ty, &slot.element, &slot.component, true);
2090 }
2091 if attrs.composite {
2092 if is_option_type(ty) {
2093 let inner_ty = option_inner_type(ty)
2094 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
2095 return Ok(quote! {
2096 let #ident = match __seg.elements.get(#idx) {
2097 ::core::option::Option::Some(__e) => {
2098 let __cows = __e.components.iter()
2099 .map(|(s, _)| ::std::borrow::Cow::Borrowed(s.as_str()))
2100 .collect::<::std::vec::Vec<::std::borrow::Cow<'_, str>>>();
2101 ::core::option::Option::Some(
2102 <#inner_ty as ::edifact_rs::EdifactCompositeDeserialize>::edifact_deserialize_composite(
2103 ::edifact_rs::CompositeElement::from_slice(&__cows)
2104 )?
2105 )
2106 }
2107 ::core::option::Option::None => ::core::option::Option::None,
2108 };
2109 });
2110 }
2111 return Ok(quote! {
2112 let #ident = {
2113 let __cows = __seg.elements.get(#idx)
2114 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
2115 tag: #seg_tag.to_owned(),
2116 element_index: #idx,
2117 })?
2118 .components.iter()
2119 .map(|(s, _)| ::std::borrow::Cow::Borrowed(s.as_str()))
2120 .collect::<::std::vec::Vec<::std::borrow::Cow<'_, str>>>();
2121 <#ty as ::edifact_rs::EdifactCompositeDeserialize>::edifact_deserialize_composite(
2122 ::edifact_rs::CompositeElement::from_slice(&__cows)
2123 )?
2124 };
2125 });
2126 }
2127 let comp = &slot.component;
2128 let value_expr_owned = if slot.has_component {
2129 quote! { __seg.component_str(#idx, #comp) }
2130 } else {
2131 quote! { __seg.element_str(#idx) }
2132 };
2133 let names_component = &slot.names_component;
2136 let missing_required_err_owned = quote! {
2137 if #names_component {
2138 ::edifact_rs::EdifactError::MissingRequiredComponent {
2139 tag: #seg_tag.to_owned(),
2140 element_index: #idx,
2141 component_index: #comp,
2142 }
2143 } else {
2144 ::edifact_rs::EdifactError::MissingRequiredElement {
2145 tag: #seg_tag.to_owned(),
2146 element_index: #idx,
2147 }
2148 }
2149 };
2150 Ok(if is_option_type(ty) {
2151 if let Some(inner_ty) = option_inner_type(ty) {
2152 if attrs.required {
2153 if is_str_like(inner_ty) {
2157 quote! {
2158 let #ident = ::core::option::Option::Some(
2159 #value_expr_owned
2160 .filter(|__s| !__s.is_empty())
2161 .ok_or_else(|| #missing_required_err_owned)?
2162 .to_owned()
2163 );
2164 }
2165 } else {
2166 quote! {
2167 let #ident = ::core::option::Option::Some(
2168 #value_expr_owned
2169 .filter(|__s| !__s.is_empty())
2170 .ok_or_else(|| #missing_required_err_owned)?
2171 .parse::<#inner_ty>()
2172 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })?
2173 );
2174 }
2175 }
2176 } else if is_str_like(inner_ty) {
2177 quote! {
2178 let #ident = #value_expr_owned
2179 .filter(|__s| !__s.is_empty())
2180 .map(::std::string::String::from);
2181 }
2182 } else {
2183 quote! {
2184 let #ident = #value_expr_owned
2185 .filter(|__s| !__s.is_empty())
2186 .map(|__s| __s.parse::<#inner_ty>()
2187 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })
2188 )
2189 .transpose()?;
2190 }
2191 }
2192 } else {
2193 quote! {
2195 let #ident = #value_expr_owned
2196 .filter(|__s| !__s.is_empty())
2197 .map(::std::string::String::from);
2198 }
2199 }
2200 } else if is_str_like(ty) {
2201 quote! {
2202 let #ident = #value_expr_owned
2203 .filter(|__s| !__s.is_empty())
2204 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
2205 tag: #seg_tag.to_owned(),
2206 element_index: #idx,
2207 })?
2208 .to_owned();
2209 }
2210 } else {
2211 quote! {
2212 let #ident = #value_expr_owned
2213 .filter(|__s| !__s.is_empty())
2214 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
2215 tag: #seg_tag.to_owned(),
2216 element_index: #idx,
2217 })?
2218 .parse::<#ty>()
2219 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })?;
2220 }
2221 })
2222 })
2223 .collect::<syn::Result<_>>()?;
2224
2225 let owned_body = quote! {
2226 #slot_prelude
2227 let __seg = #find_seg_owned
2228 .ok_or_else(|| ::edifact_rs::EdifactError::MissingSegment {
2229 tag: #seg_tag.to_owned(),
2230 expected_position: "message body".to_owned(),
2231 })?;
2232 #qualifier_guard
2233 #(#field_inits_owned)*
2234 ::core::result::Result::Ok(Self { #(#field_names),* })
2235 };
2236
2237 (body, owned_body, seg_tag_impl)
2238 } else {
2239 let field_inits: Vec<TokenStream2> = field_data
2241 .iter()
2242 .map(|(ident, ty, attrs)| -> syn::Result<TokenStream2> {
2243 Ok(if let Some(qual) = &attrs.qualifier {
2244 if attrs.group || is_vec_type(ty) {
2245 let inner_ty = vec_inner_type(ty)
2246 .ok_or_else(|| syn::Error::new(ident.span(), "expected Vec<T>"))?;
2247 quote! {
2248 let #ident = segments
2249 .iter()
2250 .filter(|__seg| {
2251 __seg.tag == <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG
2252 && __seg.element_str(0).unwrap_or("") == #qual
2253 })
2254 .map(|__seg| {
2255 ::edifact_rs::EdifactDeserialize::edifact_deserialize(
2256 ::core::slice::from_ref(__seg),
2257 )
2258 })
2259 .collect::<::core::result::Result<::std::vec::Vec<#inner_ty>, ::edifact_rs::EdifactError>>()?;
2260 }
2261 } else if is_option_type(ty) {
2262 let inner_ty = option_inner_type(ty)
2263 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
2264 quote! {
2265 let #ident = match ::edifact_rs::find_qualified_segment(
2266 segments,
2267 <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG,
2268 #qual,
2269 ) {
2270 ::core::option::Option::Some(__seg) => {
2271 ::core::option::Option::Some(
2272 ::edifact_rs::EdifactDeserialize::edifact_deserialize(
2273 ::core::slice::from_ref(__seg),
2274 )?
2275 )
2276 }
2277 ::core::option::Option::None => ::core::option::Option::None,
2278 };
2279 }
2280 } else {
2281 quote! {
2282 let __seg = ::edifact_rs::find_qualified_segment(
2283 segments,
2284 <#ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG,
2285 #qual,
2286 )
2287 .ok_or_else(|| ::edifact_rs::EdifactError::MissingSegment {
2288 tag: <#ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG.to_owned(),
2289 expected_position: "message body".to_owned(),
2290 })?;
2291 let #ident = ::edifact_rs::EdifactDeserialize::edifact_deserialize(
2292 ::core::slice::from_ref(__seg),
2293 )?;
2294 }
2295 }
2296 } else if attrs.group || is_vec_type(ty) {
2297 let inner_ty = vec_inner_type(ty)
2298 .ok_or_else(|| syn::Error::new(ident.span(), "expected Vec<T>"))?;
2299 quote! {
2300 let #ident = ::edifact_rs::find_segments_typed::<#inner_ty>(segments)
2301 .map(|__seg| {
2302 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize(
2303 ::core::slice::from_ref(__seg),
2304 )
2305 })
2306 .collect::<::core::result::Result<::std::vec::Vec<#inner_ty>, _>>()?;
2307 }
2308 } else if is_option_type(ty) {
2309 let inner_ty = option_inner_type(ty)
2310 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
2311 quote! {
2312 let #ident = if segments
2313 .iter()
2314 .any(|__seg| __seg.tag == <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG)
2315 {
2316 ::core::option::Option::Some(
2317 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize(segments)?
2318 )
2319 } else {
2320 ::core::option::Option::None
2321 };
2322 }
2323 } else {
2324 quote! {
2325 let #ident = ::edifact_rs::EdifactDeserialize::edifact_deserialize(segments)?;
2326 }
2327 })
2328 })
2329 .collect::<syn::Result<_>>()?;
2330
2331 let body = quote! {
2332 #(#field_inits)*
2333 ::core::result::Result::Ok(Self { #(#field_names),* })
2334 };
2335
2336 let field_inits_owned: Vec<TokenStream2> = field_data
2339 .iter()
2340 .map(|(ident, ty, attrs)| -> syn::Result<TokenStream2> {
2341 Ok(if let Some(qual) = &attrs.qualifier {
2342 if attrs.group || is_vec_type(ty) {
2343 let inner_ty = vec_inner_type(ty)
2344 .ok_or_else(|| syn::Error::new(ident.span(), "expected Vec<T>"))?;
2345 quote! {
2346 let #ident = segments
2347 .iter()
2348 .filter(|__seg| {
2349 __seg.tag == <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG
2350 && __seg.element_str(0).unwrap_or("") == #qual
2351 })
2352 .map(|__seg| {
2353 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(
2354 ::core::slice::from_ref(__seg),
2355 )
2356 })
2357 .collect::<::core::result::Result<::std::vec::Vec<#inner_ty>, ::edifact_rs::EdifactError>>()?;
2358 }
2359 } else if is_option_type(ty) {
2360 let inner_ty = option_inner_type(ty)
2361 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
2362 quote! {
2363 let #ident = match ::edifact_rs::find_qualified_segment_owned(
2364 segments,
2365 <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG,
2366 #qual,
2367 ) {
2368 ::core::option::Option::Some(__seg) => {
2369 ::core::option::Option::Some(
2370 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(
2371 ::core::slice::from_ref(__seg),
2372 )?
2373 )
2374 }
2375 ::core::option::Option::None => ::core::option::Option::None,
2376 };
2377 }
2378 } else {
2379 quote! {
2380 let __seg = ::edifact_rs::find_qualified_segment_owned(
2381 segments,
2382 <#ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG,
2383 #qual,
2384 )
2385 .ok_or_else(|| ::edifact_rs::EdifactError::MissingSegment {
2386 tag: <#ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG.to_owned(),
2387 expected_position: "message body".to_owned(),
2388 })?;
2389 let #ident = <#ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(
2390 ::core::slice::from_ref(__seg),
2391 )?;
2392 }
2393 }
2394 } else if attrs.group || is_vec_type(ty) {
2395 let inner_ty = vec_inner_type(ty)
2396 .ok_or_else(|| syn::Error::new(ident.span(), "expected Vec<T>"))?;
2397 quote! {
2398 let #ident = segments
2399 .iter()
2400 .filter(|__seg| <#inner_ty as ::edifact_rs::EdifactSegmentTag>::matches_owned_segment(__seg))
2401 .map(|__seg| {
2402 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(
2403 ::core::slice::from_ref(__seg),
2404 )
2405 })
2406 .collect::<::core::result::Result<::std::vec::Vec<#inner_ty>, _>>()?;
2407 }
2408 } else if is_option_type(ty) {
2409 let inner_ty = option_inner_type(ty)
2410 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
2411 quote! {
2412 let #ident = if segments
2413 .iter()
2414 .any(|__seg| __seg.tag == <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG)
2415 {
2416 ::core::option::Option::Some(
2417 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(segments)?
2418 )
2419 } else {
2420 ::core::option::Option::None
2421 };
2422 }
2423 } else {
2424 quote! {
2425 let #ident = <#ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(segments)?;
2426 }
2427 })
2428 })
2429 .collect::<syn::Result<_>>()?;
2430
2431 let owned_body = quote! {
2432 #(#field_inits_owned)*
2433 ::core::result::Result::Ok(Self { #(#field_names),* })
2434 };
2435
2436 (body, owned_body, quote! {})
2437 };
2438
2439 Ok(quote! {
2440 impl ::edifact_rs::EdifactDeserialize for #name {
2441 fn edifact_deserialize(
2442 segments: &[::edifact_rs::Segment<'_>],
2443 ) -> ::core::result::Result<Self, ::edifact_rs::EdifactError> {
2444 #body
2445 }
2446
2447 fn edifact_deserialize_owned(
2448 segments: &[::edifact_rs::OwnedSegment],
2449 ) -> ::core::result::Result<Self, ::edifact_rs::EdifactError> {
2450 #owned_body
2451 }
2452 }
2453 #segment_tag_impl
2454 })
2455}
2456
2457#[cfg(test)]
2458mod tests {
2459 #[test]
2473 fn trybuild_ui() {
2474 if std::env::var_os("EDIFACT_UI_TESTS").is_none() {
2475 eprintln!(
2476 "skipping derive UI suite: set EDIFACT_UI_TESTS=1 to run it \
2477 (expectations are pinned to the MSRV toolchain)"
2478 );
2479 return;
2480 }
2481 let t = trybuild::TestCases::new();
2482 t.pass("tests/ui/pass_*.rs");
2483 t.compile_fail("tests/ui/fail_*.rs");
2484 }
2485}