1#![deny(unsafe_code)]
2use proc_macro::TokenStream;
101use proc_macro2::TokenStream as TokenStream2;
102use quote::quote;
103use syn::{Data, DeriveInput, Field, Fields, Type, parse_macro_input, spanned::Spanned};
104
105#[proc_macro_derive(EdifactSerialize, attributes(edifact))]
108pub fn derive_edifact_serialize(input: TokenStream) -> TokenStream {
117 let input = parse_macro_input!(input as DeriveInput);
118 impl_serialize(&input)
119 .unwrap_or_else(|e| e.to_compile_error())
120 .into()
121}
122
123#[proc_macro_derive(EdifactDeserialize, attributes(edifact))]
124pub fn derive_edifact_deserialize(input: TokenStream) -> TokenStream {
133 let input = parse_macro_input!(input as DeriveInput);
134 impl_deserialize(&input)
135 .unwrap_or_else(|e| e.to_compile_error())
136 .into()
137}
138
139#[proc_macro_derive(EdifactCompositeDeserialize, attributes(edifact))]
140pub fn derive_edifact_composite_deserialize(input: TokenStream) -> TokenStream {
155 let input = parse_macro_input!(input as DeriveInput);
156 impl_composite_deserialize(&input)
157 .unwrap_or_else(|e| e.to_compile_error())
158 .into()
159}
160
161#[proc_macro_derive(EdifactCompositeSerialize, attributes(edifact))]
162pub fn derive_edifact_composite_serialize(input: TokenStream) -> TokenStream {
173 let input = parse_macro_input!(input as DeriveInput);
174 impl_composite_serialize(&input)
175 .unwrap_or_else(|e| e.to_compile_error())
176 .into()
177}
178
179#[derive(Clone)]
188enum Position {
189 Index(u32),
191 Code(String),
193}
194
195#[derive(Default)]
196struct StructAttrs {
197 segment: Option<String>,
199 qualifier: Option<String>,
201 qualifier_span: Option<proc_macro2::Span>,
202 qualifier_from: Option<u32>,
204 qualifier_from_span: Option<proc_macro2::Span>,
205 layout: Option<syn::Path>,
208 layout_span: Option<proc_macro2::Span>,
209}
210
211#[derive(Default)]
212struct FieldAttrs {
213 element: Option<Position>,
215 element_span: Option<proc_macro2::Span>,
216 component: Option<u32>,
218 component_span: Option<proc_macro2::Span>,
219 composite: bool,
221 composite_span: Option<proc_macro2::Span>,
222 group: bool,
224 group_span: Option<proc_macro2::Span>,
225 qualifier: Option<String>,
227 qualifier_span: Option<proc_macro2::Span>,
228 required: bool,
236 required_span: Option<proc_macro2::Span>,
237}
238
239const MAX_POSITION_INDEX: u32 = 256;
249
250fn check_index_bound(lit: &syn::LitInt, value: u32, key: &str) -> syn::Result<()> {
251 if value > MAX_POSITION_INDEX {
252 return Err(syn::Error::new(
253 lit.span(),
254 format!(
255 "`{key}` index {value} exceeds the maximum of {MAX_POSITION_INDEX}; \
256 UN/EDIFACT allows at most 99 elements per segment and 99 components per composite"
257 ),
258 ));
259 }
260 Ok(())
261}
262
263fn parse_struct_attrs(input: &DeriveInput) -> syn::Result<StructAttrs> {
264 let mut out = StructAttrs::default();
265 for attr in &input.attrs {
266 if !attr.path().is_ident("edifact") {
267 continue;
268 }
269 attr.parse_nested_meta(|meta| {
270 if meta.path.is_ident("segment") {
271 if out.segment.is_some() {
272 return Err(meta.error("duplicate `segment`"));
273 }
274 let lit = meta.value()?.parse::<syn::LitStr>()?;
275 let tag = lit.value();
276 if tag.len() != 3 || !tag.bytes().all(|b| b.is_ascii_uppercase()) {
277 return Err(syn::Error::new(
278 lit.span(),
279 format!(
280 "segment tag must be exactly 3 ASCII uppercase letters; got {tag:?}"
281 ),
282 ));
283 }
284 out.segment = Some(tag);
285 } else if meta.path.is_ident("qualifier") {
286 if out.qualifier.is_some() {
287 return Err(meta.error("duplicate `qualifier`"));
288 }
289 out.qualifier = Some(meta.value()?.parse::<syn::LitStr>()?.value());
290 out.qualifier_span = Some(meta.path.span());
291 } else if meta.path.is_ident("qualifier_from") {
292 if out.qualifier_from.is_some() {
293 return Err(meta.error("duplicate `qualifier_from`"));
294 }
295 let lit = meta.value()?.parse::<syn::LitInt>()?;
296 let idx: u32 = lit.base10_parse()?;
297 check_index_bound(&lit, idx, "qualifier_from")?;
298 out.qualifier_from = Some(idx);
299 out.qualifier_from_span = Some(meta.path.span());
300 } else if meta.path.is_ident("layout") {
301 if out.layout.is_some() {
302 return Err(meta.error("duplicate `layout`"));
303 }
304 out.layout_span = Some(meta.path.span());
305 let value = meta.value()?;
306 out.layout = Some(if value.peek(syn::LitStr) {
310 value.parse::<syn::LitStr>()?.parse()?
311 } else {
312 value.parse::<syn::Path>()?
313 });
314 } else {
315 return Err(meta.error("unknown struct-level `edifact` key; expected `segment`, `qualifier`, `qualifier_from`, or `layout`"));
316 }
317 Ok(())
318 })?;
319 }
320 if out.layout.is_some() && out.segment.is_none() {
321 return Err(syn::Error::new(
322 out.layout_span.unwrap_or_else(|| input.span()),
323 "#[edifact(layout = ...)] requires #[edifact(segment = ...)]: a layout describes one segment",
324 ));
325 }
326 if (out.qualifier.is_some() || out.qualifier_from.is_some()) && out.segment.is_none() {
327 return Err(syn::Error::new(
328 out.qualifier_span
329 .or(out.qualifier_from_span)
330 .unwrap_or_else(|| input.span()),
331 "#[edifact(qualifier = ...)] / #[edifact(qualifier_from = ...)] require #[edifact(segment = ...)]",
332 ));
333 }
334 if out.qualifier.is_some() && out.qualifier_from.is_some() {
335 return Err(syn::Error::new(
336 out.qualifier_from_span
337 .or(out.qualifier_span)
338 .unwrap_or_else(|| input.span()),
339 "use either #[edifact(qualifier = ...)] or #[edifact(qualifier_from = ...)], not both",
340 ));
341 }
342 Ok(out)
343}
344
345fn parse_field_attrs(field: &Field) -> syn::Result<FieldAttrs> {
346 let mut out = FieldAttrs::default();
347 for attr in &field.attrs {
348 if !attr.path().is_ident("edifact") {
349 continue;
350 }
351 attr.parse_nested_meta(|meta| {
352 if meta.path.is_ident("element") {
353 if out.element.is_some() {
354 return Err(meta.error("duplicate `element`"));
355 }
356 out.element_span = Some(meta.path.span());
357 let value = meta.value()?;
358 out.element = Some(if value.peek(syn::LitStr) {
359 let lit = value.parse::<syn::LitStr>()?;
360 let code = lit.value();
361 if code.is_empty() {
362 return Err(syn::Error::new(
363 lit.span(),
364 "`element` data element identifier must not be empty",
365 ));
366 }
367 Position::Code(code)
368 } else {
369 let lit = value.parse::<syn::LitInt>()?;
370 let idx: u32 = lit.base10_parse()?;
371 check_index_bound(&lit, idx, "element")?;
372 Position::Index(idx)
373 });
374 } else if meta.path.is_ident("component") {
375 if out.component.is_some() {
376 return Err(meta.error("duplicate `component`"));
377 }
378 out.component_span = Some(meta.path.span());
379 let value = meta.value()?;
380 if value.peek(syn::LitStr) {
381 let lit = value.parse::<syn::LitStr>()?;
382 return Err(syn::Error::new(
383 lit.span(),
384 "put the data element identifier in `element`: \
385 `#[edifact(element = \"3055\")]` resolves both the element and the \
386 component position from the directory",
387 ));
388 }
389 let lit = value.parse::<syn::LitInt>()?;
390 let idx: u32 = lit.base10_parse()?;
391 check_index_bound(&lit, idx, "component")?;
392 out.component = Some(idx);
393 } else if meta.path.is_ident("composite") {
394 out.composite = true;
395 out.composite_span = Some(meta.path.span());
396 } else if meta.path.is_ident("group") {
397 out.group = true;
398 out.group_span = Some(meta.path.span());
399 } else if meta.path.is_ident("qualifier") {
400 if out.qualifier.is_some() {
401 return Err(meta.error("duplicate `qualifier`"));
402 }
403 out.qualifier = Some(meta.value()?.parse::<syn::LitStr>()?.value());
404 out.qualifier_span = Some(meta.path.span());
405 } else if meta.path.is_ident("required") {
406 out.required = true;
407 out.required_span = Some(meta.path.span());
408 } else {
409 return Err(meta.error("unknown field-level `edifact` key; expected `element`, `component`, `composite`, `group`, `qualifier`, or `required`"));
410 }
411 Ok(())
412 })?;
413 }
414 Ok(out)
415}
416
417struct Slots {
421 element: TokenStream2,
423 component: TokenStream2,
425 has_component: bool,
431 names_component: TokenStream2,
442}
443
444fn resolve_slots(
452 struct_attrs: &StructAttrs,
453 field_data: &[(&syn::Ident, &Type, FieldAttrs)],
454) -> syn::Result<(TokenStream2, Vec<Slots>)> {
455 let mut consts: Vec<TokenStream2> = Vec::new();
456 let mut slots: Vec<Slots> = Vec::with_capacity(field_data.len());
457 let mut slot_entries: Vec<TokenStream2> = Vec::new();
458
459 if struct_attrs.qualifier.is_some() {
460 slot_entries.push(quote! { (0usize, 0usize) });
462 }
463
464 for (i, (ident, _, attrs)) in field_data.iter().enumerate() {
465 if attrs.group {
466 slots.push(Slots {
467 element: quote! { 0usize },
468 component: quote! { 0usize },
469 has_component: false,
470 names_component: quote! { false },
471 });
472 continue;
473 }
474 let component_index = attrs.component.unwrap_or(0);
475 let slot = match &attrs.element {
476 Some(Position::Code(code)) => {
477 let Some(layout) = &struct_attrs.layout else {
478 return Err(syn::Error::new(
479 attrs.element_span.unwrap_or_else(|| ident.span()),
480 format!(
481 "field `{ident}`: `element = \"{code}\"` addresses a UN/EDIFACT data \
482 element identifier, which needs a directory to resolve against; add \
483 #[edifact(layout = path::to::SEGMENT_DEFINITION)] to the struct"
484 ),
485 ));
486 };
487 let slot_ident = syn::Ident::new(
488 &format!("__EDIFACT_SLOT_{i}"),
489 attrs.element_span.unwrap_or_else(|| ident.span()),
490 );
491 let unknown_msg = format!(
492 "field `{ident}`: data element {code} is not defined exactly once in the \
493 segment layout — check the identifier against the directory"
494 );
495 consts.push(quote! {
496 const _: () = ::core::assert!(#layout.code_positions(#code) == 1, #unknown_msg);
497 });
498 if attrs.component.is_some() {
499 let conflict_msg = format!(
500 "field `{ident}`: `component = N` may only accompany an identifier that \
501 names a whole data element, but {code} names a component inside one"
502 );
503 consts.push(quote! {
504 const _: () =
505 ::core::assert!(#layout.component_slot(#code) == 0, #conflict_msg);
506 });
507 consts.push(quote! {
508 const #slot_ident: (usize, usize) =
512 if #layout.code_positions(#code) == 1 {
513 (#layout.element_slot(#code), #component_index as usize)
514 } else {
515 (0, 0)
516 };
517 });
518 } else {
519 consts.push(quote! {
520 const #slot_ident: (usize, usize) =
521 if #layout.code_positions(#code) == 1 {
522 (#layout.element_slot(#code), #layout.component_slot(#code))
523 } else {
524 (0, 0)
525 };
526 });
527 }
528 slot_entries.push(quote! { #slot_ident });
529 let names_component = if attrs.component.is_some() {
530 quote! { true }
533 } else {
534 quote! { #layout.code_is_component(#code) }
535 };
536 Slots {
537 element: quote! { #slot_ident.0 },
538 component: quote! { #slot_ident.1 },
539 has_component: true,
540 names_component,
541 }
542 }
543 Some(Position::Index(idx)) => {
544 let idx = *idx;
545 let explicit = attrs.component.is_some();
546 slot_entries.push(quote! { (#idx as usize, #component_index as usize) });
547 Slots {
548 element: quote! { #idx as usize },
549 component: quote! { #component_index as usize },
550 has_component: explicit,
551 names_component: quote! { #explicit },
552 }
553 }
554 None => {
555 let idx = i as u32;
557 let explicit = attrs.component.is_some();
558 slot_entries.push(quote! { (#idx as usize, #component_index as usize) });
559 Slots {
560 element: quote! { #idx as usize },
561 component: quote! { #component_index as usize },
562 has_component: explicit,
563 names_component: quote! { #explicit },
564 }
565 }
566 };
567 slots.push(slot);
568 }
569
570 if struct_attrs.layout.is_some() && !slot_entries.is_empty() {
575 let count = slot_entries.len();
576 consts.push(quote! {
577 const __EDIFACT_SLOTS: [(usize, usize); #count] = [#(#slot_entries),*];
578 const _: () = {
579 let mut i = 0;
580 while i < __EDIFACT_SLOTS.len() {
581 let mut j = i + 1;
582 while j < __EDIFACT_SLOTS.len() {
583 ::core::assert!(
584 !(__EDIFACT_SLOTS[i].0 == __EDIFACT_SLOTS[j].0
585 && __EDIFACT_SLOTS[i].1 == __EDIFACT_SLOTS[j].1),
586 "two fields resolve to the same element/component slot; \
587 give each field a distinct data element identifier or index"
588 );
589 j += 1;
590 }
591 i += 1;
592 }
593 };
594 });
595 }
596
597 Ok((quote! { #(#consts)* }, slots))
598}
599
600fn is_option_type(ty: &Type) -> bool {
603 matches!(ty, Type::Path(p) if p.path.segments.last().is_some_and(|s| s.ident == "Option"))
604}
605
606fn is_vec_type(ty: &Type) -> bool {
607 matches!(ty, Type::Path(p) if p.path.segments.last().is_some_and(|s| s.ident == "Vec"))
608}
609
610fn is_string_type(ty: &Type) -> bool {
628 let Type::Path(p) = ty else { return false };
629 if p.path.is_ident("String") {
631 return true;
632 }
633 let segs = &p.path.segments;
635 segs.len() == 3
636 && (segs[0].ident == "std" || segs[0].ident == "alloc")
637 && segs[1].ident == "string"
638 && segs[2].ident == "String"
639}
640
641fn is_str_ref_type(ty: &Type) -> bool {
643 let Type::Reference(r) = ty else { return false };
644 matches!(r.elem.as_ref(), Type::Path(p) if p.path.is_ident("str"))
645}
646
647fn is_str_like(ty: &Type) -> bool {
650 is_string_type(ty) || is_str_ref_type(ty)
651}
652
653fn option_inner_type(ty: &Type) -> Option<&Type> {
654 let Type::Path(path) = ty else { return None };
655 let seg = path.path.segments.last()?;
656 if seg.ident != "Option" {
657 return None;
658 }
659 let syn::PathArguments::AngleBracketed(args) = &seg.arguments else {
660 return None;
661 };
662 let syn::GenericArgument::Type(inner) = args.args.first()? else {
663 return None;
664 };
665 Some(inner)
666}
667
668fn vec_inner_type(ty: &Type) -> Option<&Type> {
669 let Type::Path(path) = ty else { return None };
670 let seg = path.path.segments.last()?;
671 if seg.ident != "Vec" {
672 return None;
673 }
674 let syn::PathArguments::AngleBracketed(args) = &seg.arguments else {
675 return None;
676 };
677 let syn::GenericArgument::Type(inner) = args.args.first()? else {
678 return None;
679 };
680 Some(inner)
681}
682
683fn check_duplicate_slots(
695 field_data: &[(&syn::Ident, &Type, FieldAttrs)],
696 is_segment_struct: bool,
697) -> syn::Result<()> {
698 if !is_segment_struct {
699 return Ok(());
700 }
701 let mut seen: Vec<((u32, u32), &syn::Ident)> = Vec::with_capacity(field_data.len());
702 for (i, (ident, _, attrs)) in field_data.iter().enumerate() {
703 if attrs.group {
705 continue;
706 }
707 let element = match &attrs.element {
708 Some(Position::Index(idx)) => *idx,
709 Some(Position::Code(_)) => continue,
710 None => i as u32,
711 };
712 let slot = (element, attrs.component.unwrap_or(0));
713 if let Some((_, first)) = seen.iter().find(|(s, _)| *s == slot) {
714 return Err(syn::Error::new(
715 ident.span(),
716 format!(
717 "field `{ident}` maps to element {} component {}, which is already \
718 claimed by field `{first}`; give each field a distinct \
719 `#[edifact(element = ..., component = ...)]` slot",
720 slot.0, slot.1
721 ),
722 ));
723 }
724 seen.push((slot, ident));
725 }
726 Ok(())
727}
728
729struct CompositeSlot<'a> {
733 ident: &'a syn::Ident,
734 ty: &'a Type,
735 index: u32,
736 optional: bool,
737}
738
739fn composite_slots(input: &DeriveInput) -> syn::Result<Vec<CompositeSlot<'_>>> {
746 let fields = get_named_fields(input)?;
747 let mut slots: Vec<CompositeSlot<'_>> = Vec::with_capacity(fields.named.len());
748
749 for (decl_index, field) in fields.named.iter().enumerate() {
750 let ident = field.ident.as_ref().expect("named fields checked above");
751 let attrs = parse_field_attrs(field)?;
752
753 for (present, span, key) in [
754 (attrs.element.is_some(), attrs.element_span, "element"),
755 (attrs.composite, attrs.composite_span, "composite"),
756 (attrs.group, attrs.group_span, "group"),
757 (attrs.qualifier.is_some(), attrs.qualifier_span, "qualifier"),
758 ] {
759 if present {
760 return Err(syn::Error::new(
761 span.unwrap_or_else(|| field.span()),
762 format!(
763 "`{key}` has no meaning on a composite struct field; \
764 a composite maps its fields to components, so only \
765 `component` and `required` apply"
766 ),
767 ));
768 }
769 }
770
771 let ty = &field.ty;
772 let inner = option_inner_type(ty).unwrap_or(ty);
773 if !is_string_type(inner) {
774 return Err(syn::Error::new(
775 ty.span(),
776 "composite struct fields must be `String` or `Option<String>`; \
777 a component is a single text value",
778 ));
779 }
780
781 slots.push(CompositeSlot {
782 ident,
783 ty,
784 index: attrs.component.unwrap_or(decl_index as u32),
785 optional: is_option_type(ty) && !attrs.required,
786 });
787 }
788
789 let mut seen: Vec<(u32, &syn::Ident)> = Vec::with_capacity(slots.len());
792 for slot in &slots {
793 if let Some((_, first)) = seen.iter().find(|(i, _)| *i == slot.index) {
794 return Err(syn::Error::new(
795 slot.ident.span(),
796 format!(
797 "component {} is already mapped by field `{first}`",
798 slot.index
799 ),
800 ));
801 }
802 seen.push((slot.index, slot.ident));
803 }
804 Ok(slots)
805}
806
807fn impl_composite_deserialize(input: &DeriveInput) -> syn::Result<TokenStream2> {
808 let name = &input.ident;
809 let slots = composite_slots(input)?;
810
811 let assignments = slots.iter().map(|slot| {
812 let CompositeSlot {
813 ident,
814 ty,
815 index,
816 optional,
817 } = slot;
818 let idx = *index as usize;
819 if *optional {
820 quote! {
821 #ident: match __composite.get(#idx) {
822 Some(v) if !v.is_empty() => Some(::std::string::String::from(v)),
823 _ => None,
824 },
825 }
826 } else {
827 let build = if is_option_type(ty) {
830 quote! { Some(::std::string::String::from(__value)) }
831 } else {
832 quote! { ::std::string::String::from(__value) }
833 };
834 quote! {
835 #ident: {
836 let __value = __composite
837 .get(#idx)
838 .filter(|v| !v.is_empty())
839 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredComponent {
840 tag: ::std::string::String::from(stringify!(#name)),
841 element_index: 0,
842 component_index: #idx,
843 })?;
844 #build
845 },
846 }
847 }
848 });
849
850 Ok(quote! {
851 impl ::edifact_rs::EdifactCompositeDeserialize for #name {
852 fn edifact_deserialize_composite(
853 __composite: ::edifact_rs::CompositeElement<'_>,
854 ) -> ::core::result::Result<Self, ::edifact_rs::EdifactError> {
855 ::core::result::Result::Ok(Self {
856 #(#assignments)*
857 })
858 }
859 }
860 })
861}
862
863fn impl_composite_serialize(input: &DeriveInput) -> syn::Result<TokenStream2> {
864 let name = &input.ident;
865 let slots = composite_slots(input)?;
866
867 let mut ordered: Vec<&CompositeSlot<'_>> = slots.iter().collect();
870 ordered.sort_by_key(|slot| slot.index);
871 let highest = ordered.last().map_or(0, |slot| slot.index);
872
873 let emits = (0..=highest).map(|index| {
874 let value = match ordered.iter().find(|slot| slot.index == index) {
875 Some(slot) => {
876 let ident = slot.ident;
877 if is_option_type(slot.ty) {
878 quote! { self.#ident.as_deref().unwrap_or("") }
879 } else {
880 quote! { self.#ident.as_str() }
881 }
882 }
883 None => quote! { "" },
884 };
885 if index == 0 {
887 quote! { __emitter.emit(::edifact_rs::EdifactEvent::Element { value: #value })?; }
888 } else {
889 quote! {
890 __emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: #value })?;
891 }
892 }
893 });
894
895 let body = if slots.is_empty() {
897 quote! { __emitter.emit(::edifact_rs::EdifactEvent::Element { value: "" })?; }
898 } else {
899 quote! { #(#emits)* }
900 };
901
902 Ok(quote! {
903 impl ::edifact_rs::EdifactCompositeSerialize for #name {
904 fn edifact_serialize_composite<__E: ::edifact_rs::EventEmitter>(
905 &self,
906 __emitter: &mut __E,
907 ) -> ::core::result::Result<(), ::edifact_rs::EdifactError> {
908 #body
909 ::core::result::Result::Ok(())
910 }
911 }
912 })
913}
914
915fn get_named_fields(input: &DeriveInput) -> syn::Result<&syn::FieldsNamed> {
916 if !input.generics.params.is_empty() {
917 return Err(syn::Error::new(
918 input.generics.params.span(),
919 "EdifactSerialize/EdifactDeserialize do not support generic structs",
920 ));
921 }
922 match &input.data {
923 Data::Struct(s) => match &s.fields {
924 Fields::Named(f) => Ok(f),
925 _ => Err(syn::Error::new(
926 input.span(),
927 "EdifactSerialize/EdifactDeserialize only support structs with named fields",
928 )),
929 },
930 _ => Err(syn::Error::new(
931 input.span(),
932 "EdifactSerialize/EdifactDeserialize only support structs",
933 )),
934 }
935}
936
937fn validate_field_attrs(
938 ident: &syn::Ident,
939 ty: &Type,
940 attrs: &FieldAttrs,
941 is_segment_struct: bool,
942) -> syn::Result<()> {
943 if attrs.group && !is_vec_type(ty) {
944 return Err(syn::Error::new(
945 attrs.group_span.unwrap_or_else(|| ident.span()),
946 format!("field `{ident}`: #[edifact(group)] requires Vec<T>"),
947 ));
948 }
949 if attrs.group && (attrs.element.is_some() || attrs.component.is_some()) {
950 return Err(syn::Error::new(
951 attrs.group_span.unwrap_or_else(|| ident.span()),
952 format!(
953 "field `{ident}`: #[edifact(group)] cannot be combined with element/component positioning"
954 ),
955 ));
956 }
957 if attrs.composite && attrs.component.is_some() {
958 return Err(syn::Error::new(
959 attrs.component_span.unwrap_or_else(|| ident.span()),
960 format!(
961 "field `{ident}`: #[edifact(component = ...)] cannot be combined with #[edifact(composite)]"
962 ),
963 ));
964 }
965 if attrs.composite && attrs.group {
966 return Err(syn::Error::new(
967 attrs.composite_span.unwrap_or_else(|| ident.span()),
968 format!(
969 "field `{ident}`: #[edifact(composite)] cannot be combined with #[edifact(group)]"
970 ),
971 ));
972 }
973 if is_segment_struct && attrs.group {
974 return Err(syn::Error::new(
975 attrs.group_span.unwrap_or_else(|| ident.span()),
976 format!("field `{ident}`: #[edifact(group)] is only valid on message structs"),
977 ));
978 }
979 if !is_segment_struct && (attrs.element.is_some() || attrs.component.is_some()) {
980 return Err(syn::Error::new(
981 attrs
982 .element_span
983 .or(attrs.component_span)
984 .unwrap_or_else(|| ident.span()),
985 format!(
986 "field `{ident}`: element/component positioning is only valid on segment structs"
987 ),
988 ));
989 }
990 if !is_segment_struct && attrs.composite {
991 return Err(syn::Error::new(
992 attrs.composite_span.unwrap_or_else(|| ident.span()),
993 format!("field `{ident}`: #[edifact(composite)] is only valid on segment structs"),
994 ));
995 }
996 if is_segment_struct && attrs.qualifier.is_some() {
997 return Err(syn::Error::new(
998 attrs.qualifier_span.unwrap_or_else(|| ident.span()),
999 format!(
1000 "field `{ident}`: #[edifact(qualifier = ...)] is only valid on message struct fields"
1001 ),
1002 ));
1003 }
1004 if attrs.qualifier.is_some() && attrs.group && !is_vec_type(ty) {
1005 return Err(syn::Error::new(
1006 attrs.qualifier_span.unwrap_or_else(|| ident.span()),
1007 format!("field `{ident}`: qualifier-constrained groups must be Vec<T>"),
1008 ));
1009 }
1010 if attrs.required && !is_option_type(ty) {
1011 return Err(syn::Error::new(
1012 attrs.required_span.unwrap_or_else(|| ident.span()),
1013 format!(
1014 "field `{ident}`: #[edifact(required)] only applies to Option<T> fields; \
1015 non-Option fields are always required"
1016 ),
1017 ));
1018 }
1019 if attrs.required && attrs.composite {
1020 return Err(syn::Error::new(
1021 attrs.required_span.unwrap_or_else(|| ident.span()),
1022 format!(
1023 "field `{ident}`: #[edifact(required)] cannot be combined with \
1024 #[edifact(composite)]; use a non-optional field type to require the \
1025 composite element"
1026 ),
1027 ));
1028 }
1029 if attrs.required && !is_segment_struct {
1030 return Err(syn::Error::new(
1031 attrs.required_span.unwrap_or_else(|| ident.span()),
1032 format!(
1033 "field `{ident}`: #[edifact(required)] is only valid on segment struct \
1034 element fields; to require a segment in a message struct, use a \
1035 non-optional field type"
1036 ),
1037 ));
1038 }
1039 Ok(())
1040}
1041
1042fn static_element_index(attrs: &FieldAttrs, decl_index: usize) -> u32 {
1049 match &attrs.element {
1050 Some(Position::Index(idx)) => *idx,
1051 _ => decl_index as u32,
1052 }
1053}
1054
1055fn impl_serialize_sparse(
1066 name: &syn::Ident,
1067 seg_tag: &str,
1068 struct_attrs: &StructAttrs,
1069 field_data: &[(&syn::Ident, &Type, FieldAttrs)],
1070 slots: &[Slots],
1071 slot_prelude: &TokenStream2,
1072) -> TokenStream2 {
1073 let mut stmts: Vec<TokenStream2> = Vec::new();
1074
1075 if let Some(qual) = &struct_attrs.qualifier {
1076 stmts.push(quote! {
1077 __parts.push((0usize, 0usize, ::std::borrow::Cow::Borrowed(#qual)));
1078 });
1079 }
1080
1081 for ((ident, ty, attrs), slot) in field_data.iter().zip(slots) {
1082 let (element, component) = (&slot.element, &slot.component);
1083 if attrs.composite {
1084 let serialize_composite = if is_option_type(ty) {
1087 quote! {
1088 if let ::core::option::Option::Some(__v) = &self.#ident {
1089 ::edifact_rs::EdifactCompositeSerialize::edifact_serialize_composite(
1090 __v, &mut __sub,
1091 )?;
1092 }
1093 }
1094 } else {
1095 quote! {
1096 ::edifact_rs::EdifactCompositeSerialize::edifact_serialize_composite(
1097 &self.#ident, &mut __sub,
1098 )?;
1099 }
1100 };
1101 stmts.push(quote! {
1102 {
1103 let mut __sub = ::edifact_rs::VecEmitter::default();
1104 #serialize_composite
1105 let mut __comp = 0usize;
1106 let mut __any = false;
1107 for __event in __sub.events {
1108 match __event {
1109 ::edifact_rs::OwnedEdifactEvent::Element { value } => {
1110 __comp = 0;
1111 __any = true;
1112 __parts.push((#element, 0usize, ::std::borrow::Cow::Owned(value)));
1113 }
1114 ::edifact_rs::OwnedEdifactEvent::ComponentElement { value } => {
1115 __comp += 1;
1116 __any = true;
1117 __parts.push((#element, __comp, ::std::borrow::Cow::Owned(value)));
1118 }
1119 _ => {}
1120 }
1121 }
1122 if !__any {
1123 __parts.push((#element, 0usize, ::std::borrow::Cow::Borrowed("")));
1124 }
1125 }
1126 });
1127 continue;
1128 }
1129
1130 let value_expr = if is_option_type(ty) {
1131 let inner_is_str = option_inner_type(ty).is_some_and(is_str_like);
1132 if inner_is_str {
1133 quote! {
1134 match &self.#ident {
1135 ::core::option::Option::Some(__v) => ::std::borrow::Cow::Borrowed(__v.as_str()),
1136 ::core::option::Option::None => ::std::borrow::Cow::Borrowed(""),
1137 }
1138 }
1139 } else {
1140 quote! {
1141 match &self.#ident {
1142 ::core::option::Option::Some(__v) => {
1143 ::std::borrow::Cow::Owned(::std::string::ToString::to_string(__v))
1144 }
1145 ::core::option::Option::None => ::std::borrow::Cow::Borrowed(""),
1146 }
1147 }
1148 }
1149 } else if is_string_type(ty) {
1150 quote! { ::std::borrow::Cow::Borrowed(self.#ident.as_str()) }
1151 } else if is_str_ref_type(ty) {
1152 quote! { ::std::borrow::Cow::Borrowed(self.#ident) }
1153 } else {
1154 quote! { ::std::borrow::Cow::Owned(::std::string::ToString::to_string(&self.#ident)) }
1155 };
1156
1157 stmts.push(quote! {
1158 __parts.push((#element, #component, #value_expr));
1159 });
1160 }
1161
1162 let capacity = field_data.len() + usize::from(struct_attrs.qualifier.is_some());
1163
1164 quote! {
1165 impl ::edifact_rs::EdifactSerialize for #name {
1166 fn edifact_serialize<__E: ::edifact_rs::EventEmitter>(
1167 &self,
1168 emitter: &mut __E,
1169 ) -> ::core::result::Result<(), ::edifact_rs::EdifactError> {
1170 #slot_prelude
1171 let mut __parts: ::std::vec::Vec<(
1172 usize,
1173 usize,
1174 ::std::borrow::Cow<'_, str>,
1175 )> = ::std::vec::Vec::with_capacity(#capacity);
1176 #(#stmts)*
1177 ::edifact_rs::emit_sparse_segment(emitter, #seg_tag, &mut __parts)
1178 }
1179 }
1180 }
1181}
1182
1183fn impl_serialize(input: &DeriveInput) -> syn::Result<TokenStream2> {
1184 let name = &input.ident;
1185 let struct_attrs = parse_struct_attrs(input)?;
1186 let fields = get_named_fields(input)?;
1187 let is_segment_struct = struct_attrs.segment.is_some();
1188
1189 let field_data: Vec<(&syn::Ident, &Type, FieldAttrs)> = fields
1191 .named
1192 .iter()
1193 .map(|f| {
1194 let attrs = parse_field_attrs(f)?;
1195 let ident = f
1196 .ident
1197 .as_ref()
1198 .ok_or_else(|| syn::Error::new_spanned(f, "only named fields are supported"))?;
1199 validate_field_attrs(ident, &f.ty, &attrs, is_segment_struct)?;
1200 Ok((ident, &f.ty, attrs))
1201 })
1202 .collect::<syn::Result<_>>()?;
1203 check_duplicate_slots(&field_data, is_segment_struct)?;
1204 let (slot_prelude, slots) = resolve_slots(&struct_attrs, &field_data)?;
1205 let uses_code_slots = field_data
1206 .iter()
1207 .any(|(_, _, attrs)| matches!(attrs.element, Some(Position::Code(_))));
1208
1209 let body = if let Some(seg_tag) = &struct_attrs.segment {
1210 if uses_code_slots {
1211 return Ok(impl_serialize_sparse(
1212 name,
1213 seg_tag,
1214 &struct_attrs,
1215 &field_data,
1216 &slots,
1217 &slot_prelude,
1218 ));
1219 }
1220 enum Cell {
1235 Qualifier,
1237 Field(usize),
1239 }
1240
1241 let mut grid: std::collections::BTreeMap<u32, std::collections::BTreeMap<u32, Cell>> =
1242 std::collections::BTreeMap::new();
1243
1244 if struct_attrs.qualifier.is_some() {
1245 for (i, (ident, _, attrs)) in field_data.iter().enumerate() {
1247 let elem = static_element_index(attrs, i);
1248 let comp = attrs.component.unwrap_or(0);
1249 if elem == 0 && comp == 0 {
1250 return Err(syn::Error::new(
1251 attrs
1252 .element_span
1253 .or(attrs.component_span)
1254 .unwrap_or_else(|| ident.span()),
1255 format!(
1256 "field `{ident}`: cannot use #[edifact(qualifier = ...)] with a field at element = 0 without component >= 1; the qualifier occupies component 0"
1257 ),
1258 ));
1259 }
1260 }
1261 grid.entry(0).or_default().insert(0, Cell::Qualifier);
1262 }
1263
1264 for (i, (_, _, attrs)) in field_data.iter().enumerate() {
1265 if attrs.group {
1268 continue;
1269 }
1270 let element = static_element_index(attrs, i);
1271 let component = attrs.component.unwrap_or(0);
1272 grid.entry(element)
1274 .or_default()
1275 .insert(component, Cell::Field(i));
1276 }
1277
1278 let empty_element = quote! {
1279 emitter.emit(::edifact_rs::EdifactEvent::Element { value: "" })?;
1280 };
1281 let empty_component = quote! {
1282 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: "" })?;
1283 };
1284
1285 let mut elem_stmts: Vec<TokenStream2> = Vec::new();
1286 if let Some(&max_element) = grid.keys().max() {
1287 for element in 0..=max_element {
1288 let Some(components) = grid.get(&element) else {
1289 elem_stmts.push(empty_element.clone());
1291 continue;
1292 };
1293 let max_component = components.keys().max().copied().unwrap_or(0);
1294 for component in 0..=max_component {
1295 match components.get(&component) {
1296 Some(Cell::Qualifier) => {
1297 let qual = struct_attrs.qualifier.as_deref().unwrap_or("");
1298 elem_stmts.push(quote! {
1299 emitter.emit(::edifact_rs::EdifactEvent::Element { value: #qual })?;
1300 });
1301 }
1302 Some(Cell::Field(index)) => {
1303 let (ident, ty, attrs) = &field_data[*index];
1304 if attrs.composite {
1305 elem_stmts.push(emit_composite_field(ident, ty));
1308 } else if component == 0 {
1309 elem_stmts.push(emit_element(ident, ty));
1310 } else {
1311 elem_stmts.push(emit_component_element(ident, ty));
1312 }
1313 }
1314 None if component == 0 => elem_stmts.push(empty_element.clone()),
1315 None => elem_stmts.push(empty_component.clone()),
1316 }
1317 }
1318 }
1319 }
1320
1321 quote! {
1322 emitter.emit(::edifact_rs::EdifactEvent::StartSegment { tag: #seg_tag })?;
1323 #(#elem_stmts)*
1324 emitter.emit(::edifact_rs::EdifactEvent::EndSegment)?;
1325 }
1326 } else {
1327 let stmts: Vec<TokenStream2> = field_data
1329 .iter()
1330 .map(|(ident, ty, attrs)| {
1331 if attrs.group || is_vec_type(ty) {
1332 quote! {
1333 for __item in &self.#ident {
1334 ::edifact_rs::EdifactSerialize::edifact_serialize(__item, emitter)?;
1335 }
1336 }
1337 } else {
1338 quote! {
1339 ::edifact_rs::EdifactSerialize::edifact_serialize(&self.#ident, emitter)?;
1340 }
1341 }
1342 })
1343 .collect();
1344 quote! { #(#stmts)* }
1345 };
1346
1347 Ok(quote! {
1348 impl ::edifact_rs::EdifactSerialize for #name {
1349 fn edifact_serialize<__E: ::edifact_rs::EventEmitter>(
1350 &self,
1351 emitter: &mut __E,
1352 ) -> ::core::result::Result<(), ::edifact_rs::EdifactError> {
1353 #body
1354 ::core::result::Result::Ok(())
1355 }
1356 }
1357 })
1358}
1359
1360fn emit_element(ident: &syn::Ident, ty: &Type) -> TokenStream2 {
1365 if is_option_type(ty) {
1366 let inner_is_str = option_inner_type(ty).is_some_and(is_str_like);
1367 if inner_is_str {
1368 quote! {
1369 match &self.#ident {
1370 ::core::option::Option::Some(__v) => {
1371 emitter.emit(::edifact_rs::EdifactEvent::Element { value: __v.as_str() })?;
1372 }
1373 ::core::option::Option::None => {
1374 emitter.emit(::edifact_rs::EdifactEvent::Element { value: "" })?;
1375 }
1376 }
1377 }
1378 } else {
1379 quote! {
1380 match &self.#ident {
1381 ::core::option::Option::Some(__v) => {
1382 let __s = ::std::string::ToString::to_string(__v);
1383 emitter.emit(::edifact_rs::EdifactEvent::Element { value: &__s })?;
1384 }
1385 ::core::option::Option::None => {
1386 emitter.emit(::edifact_rs::EdifactEvent::Element { value: "" })?;
1387 }
1388 }
1389 }
1390 }
1391 } else if is_string_type(ty) {
1392 quote! {
1393 emitter.emit(::edifact_rs::EdifactEvent::Element { value: self.#ident.as_str() })?;
1394 }
1395 } else if is_str_ref_type(ty) {
1396 quote! {
1397 emitter.emit(::edifact_rs::EdifactEvent::Element { value: self.#ident })?;
1398 }
1399 } else {
1400 quote! {
1401 {
1402 let __s = ::std::string::ToString::to_string(&self.#ident);
1403 emitter.emit(::edifact_rs::EdifactEvent::Element { value: &__s })?;
1404 }
1405 }
1406 }
1407}
1408
1409fn emit_component_element(ident: &syn::Ident, ty: &Type) -> TokenStream2 {
1413 if is_option_type(ty) {
1414 let inner_is_str = option_inner_type(ty).is_some_and(is_str_like);
1415 if inner_is_str {
1416 quote! {
1417 match &self.#ident {
1418 ::core::option::Option::Some(__v) => {
1419 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: __v.as_str() })?;
1420 }
1421 ::core::option::Option::None => {
1422 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: "" })?;
1423 }
1424 }
1425 }
1426 } else {
1427 quote! {
1428 match &self.#ident {
1429 ::core::option::Option::Some(__v) => {
1430 let __s = ::std::string::ToString::to_string(__v);
1431 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: &__s })?;
1432 }
1433 ::core::option::Option::None => {
1434 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: "" })?;
1435 }
1436 }
1437 }
1438 }
1439 } else if is_string_type(ty) {
1440 quote! {
1441 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: self.#ident.as_str() })?;
1442 }
1443 } else if is_str_ref_type(ty) {
1444 quote! {
1445 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: self.#ident })?;
1446 }
1447 } else {
1448 quote! {
1449 {
1450 let __s = ::std::string::ToString::to_string(&self.#ident);
1451 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: &__s })?;
1452 }
1453 }
1454 }
1455}
1456
1457fn emit_composite_field(ident: &syn::Ident, ty: &Type) -> TokenStream2 {
1459 if is_option_type(ty) {
1460 quote! {
1461 match &self.#ident {
1462 ::core::option::Option::Some(__v) => {
1463 ::edifact_rs::EdifactCompositeSerialize::edifact_serialize_composite(__v, emitter)?;
1464 }
1465 ::core::option::Option::None => {
1466 emitter.emit(::edifact_rs::EdifactEvent::Element { value: "" })?;
1467 }
1468 }
1469 }
1470 } else {
1471 quote! {
1472 ::edifact_rs::EdifactCompositeSerialize::edifact_serialize_composite(&self.#ident, emitter)?;
1473 }
1474 }
1475}
1476
1477fn impl_deserialize(input: &DeriveInput) -> syn::Result<TokenStream2> {
1480 let name = &input.ident;
1481 let struct_attrs = parse_struct_attrs(input)?;
1482 let fields = get_named_fields(input)?;
1483 let is_segment_struct = struct_attrs.segment.is_some();
1484
1485 let field_data: Vec<(&syn::Ident, &Type, FieldAttrs)> = fields
1486 .named
1487 .iter()
1488 .map(|f| {
1489 let attrs = parse_field_attrs(f)?;
1490 let ident = f
1491 .ident
1492 .as_ref()
1493 .ok_or_else(|| syn::Error::new_spanned(f, "only named fields are supported"))?;
1494 validate_field_attrs(ident, &f.ty, &attrs, is_segment_struct)?;
1495 Ok((ident, &f.ty, attrs))
1496 })
1497 .collect::<syn::Result<_>>()?;
1498 check_duplicate_slots(&field_data, is_segment_struct)?;
1499 let (slot_prelude, slots) = resolve_slots(&struct_attrs, &field_data)?;
1500
1501 let field_names: Vec<&syn::Ident> = field_data.iter().map(|(id, _, _)| *id).collect();
1502
1503 let (body, owned_body, segment_tag_impl) = if let Some(seg_tag) = &struct_attrs.segment {
1504 let qualifier_guard = if let Some(qual) = &struct_attrs.qualifier {
1506 quote! {
1507 if __seg.element_str(0).unwrap_or("") != #qual {
1508 return ::core::result::Result::Err(
1509 ::edifact_rs::EdifactError::MissingRequiredElement {
1510 tag: #seg_tag.to_owned(),
1511 element_index: 0,
1512 }
1513 );
1514 }
1515 }
1516 } else if let Some(idx) = struct_attrs.qualifier_from {
1517 quote! {
1518 match __seg.element_str(#idx as usize) {
1522 ::core::option::Option::None => return ::core::result::Result::Err(
1523 ::edifact_rs::EdifactError::MissingRequiredElement {
1524 tag: #seg_tag.to_owned(),
1525 element_index: #idx as usize,
1526 }
1527 ),
1528 ::core::option::Option::Some("") => return ::core::result::Result::Err(
1529 ::edifact_rs::EdifactError::InvalidFieldValue {
1530 tag: #seg_tag.to_owned(),
1531 element_index: #idx as usize,
1532 value: ::std::string::String::new(),
1533 }
1534 ),
1535 ::core::option::Option::Some(__qual_val) => { let _ = __qual_val; }
1536 }
1537 }
1538 } else {
1539 quote! {}
1540 };
1541
1542 let find_seg = if let Some(qual) = &struct_attrs.qualifier {
1543 quote! {
1544 ::edifact_rs::find_qualified_segment(segments, #seg_tag, #qual)
1545 }
1546 } else {
1547 quote! {
1548 ::edifact_rs::find_segment(segments, #seg_tag)
1549 }
1550 };
1551
1552 let field_inits: Vec<TokenStream2> = field_data
1553 .iter()
1554 .zip(slots.iter())
1555 .map(|((ident, ty, attrs), slot)| -> syn::Result<TokenStream2> {
1556 let idx = &slot.element;
1557 if attrs.composite {
1558 if is_option_type(ty) {
1559 let inner_ty = option_inner_type(ty)
1560 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1561 return Ok(quote! {
1562 let #ident = match ::edifact_rs::composite_element(__seg, #idx) {
1563 ::core::option::Option::Some(__composite) => {
1564 ::core::option::Option::Some(
1565 <#inner_ty as ::edifact_rs::EdifactCompositeDeserialize>::edifact_deserialize_composite(__composite)?
1566 )
1567 }
1568 ::core::option::Option::None => ::core::option::Option::None,
1569 };
1570 });
1571 }
1572 return Ok(quote! {
1573 let #ident = <#ty as ::edifact_rs::EdifactCompositeDeserialize>::edifact_deserialize_composite(
1574 ::edifact_rs::composite_element(__seg, #idx).ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
1575 tag: #seg_tag.to_owned(),
1576 element_index: #idx,
1577 })?
1578 )?;
1579 });
1580 }
1581 let comp = &slot.component;
1582 let value_expr = if slot.has_component {
1583 quote! {
1584 __seg.get_element(#idx).and_then(|__e| __e.get_component(#comp))
1585 }
1586 } else {
1587 quote! { __seg.element_str(#idx) }
1588 };
1589 let names_component = &slot.names_component;
1593 let missing_required_err = quote! {
1594 if #names_component {
1595 ::edifact_rs::EdifactError::MissingRequiredComponent {
1596 tag: #seg_tag.to_owned(),
1597 element_index: #idx,
1598 component_index: #comp,
1599 }
1600 } else {
1601 ::edifact_rs::EdifactError::MissingRequiredElement {
1602 tag: #seg_tag.to_owned(),
1603 element_index: #idx,
1604 }
1605 }
1606 };
1607 Ok(if is_option_type(ty) {
1608 let inner_ty = option_inner_type(ty);
1609 let inner_is_str = inner_ty.is_some_and(is_str_like);
1610 if attrs.required {
1611 if inner_is_str {
1615 quote! {
1616 let #ident = ::core::option::Option::Some(
1617 #value_expr
1618 .filter(|__s| !__s.is_empty())
1619 .ok_or_else(|| #missing_required_err)?
1620 .to_owned()
1621 );
1622 }
1623 } else {
1624 let inner_ty = inner_ty
1625 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1626 quote! {
1627 let #ident = ::core::option::Option::Some(
1628 #value_expr
1629 .filter(|__s| !__s.is_empty())
1630 .ok_or_else(|| #missing_required_err)?
1631 .parse::<#inner_ty>()
1632 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })?
1633 );
1634 }
1635 }
1636 } else if inner_is_str {
1637 quote! {
1638 let #ident = #value_expr
1639 .filter(|__s| !__s.is_empty())
1640 .map(::std::string::String::from);
1641 }
1642 } else {
1643 let inner_ty = inner_ty
1644 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1645 quote! {
1646 let #ident = #value_expr
1647 .filter(|__s| !__s.is_empty())
1648 .map(|__s| __s.parse::<#inner_ty>()
1649 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })
1650 )
1651 .transpose()?;
1652 }
1653 }
1654 } else if is_str_like(ty) {
1655 quote! {
1656 let #ident = #value_expr
1657 .filter(|__s| !__s.is_empty())
1658 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
1659 tag: #seg_tag.to_owned(),
1660 element_index: #idx,
1661 })?
1662 .to_owned();
1663 }
1664 } else {
1665 quote! {
1666 let #ident = #value_expr
1667 .filter(|__s| !__s.is_empty())
1668 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
1669 tag: #seg_tag.to_owned(),
1670 element_index: #idx,
1671 })?
1672 .parse::<#ty>()
1673 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })?;
1674 }
1675 })
1676 })
1677 .collect::<syn::Result<_>>()?;
1678
1679 let body = quote! {
1680 #slot_prelude
1681 let __seg = #find_seg
1682 .ok_or_else(|| ::edifact_rs::EdifactError::MissingSegment {
1683 tag: #seg_tag.to_owned(),
1684 expected_position: "message body".to_owned(),
1685 })?;
1686 #qualifier_guard
1687 #(#field_inits)*
1688 ::core::result::Result::Ok(Self { #(#field_names),* })
1689 };
1690
1691 let qualifier_match = if let Some(qual) = &struct_attrs.qualifier {
1698 quote! {
1699 const QUALIFIER_PATTERN: ::core::option::Option<&'static str> =
1700 ::core::option::Option::Some(#qual);
1701 }
1702 } else if let Some(idx) = struct_attrs.qualifier_from {
1703 quote! {
1707 fn matches_segment(seg: &::edifact_rs::Segment<'_>) -> bool {
1708 seg.tag == Self::SEGMENT_TAG
1709 && !seg.element_str(#idx as usize).unwrap_or("").is_empty()
1710 }
1711
1712 fn matches_owned_segment(seg: &::edifact_rs::OwnedSegment) -> bool {
1713 seg.tag == Self::SEGMENT_TAG
1714 && !seg
1715 .elements
1716 .get(#idx as usize)
1717 .and_then(|e| e.components.first())
1718 .map(|(c, _)| c.as_str())
1719 .unwrap_or("")
1720 .is_empty()
1721 }
1722 }
1723 } else {
1724 quote! {}
1725 };
1726
1727 let seg_tag_impl = quote! {
1728 impl ::edifact_rs::EdifactSegmentTag for #name {
1729 const SEGMENT_TAG: &'static str = #seg_tag;
1730 #qualifier_match
1731 }
1732 };
1733
1734 let find_seg_owned = if let Some(qual) = &struct_attrs.qualifier {
1737 quote! {
1738 ::edifact_rs::find_qualified_segment_owned(segments, #seg_tag, #qual)
1739 }
1740 } else {
1741 quote! {
1742 ::edifact_rs::find_segment_owned(segments, #seg_tag)
1743 }
1744 };
1745
1746 let field_inits_owned: Vec<TokenStream2> = field_data
1747 .iter()
1748 .zip(slots.iter())
1749 .map(|((ident, ty, attrs), slot)| -> syn::Result<TokenStream2> {
1750 let idx = &slot.element;
1751 if attrs.composite {
1752 if is_option_type(ty) {
1753 let inner_ty = option_inner_type(ty)
1754 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1755 return Ok(quote! {
1756 let #ident = match __seg.elements.get(#idx) {
1757 ::core::option::Option::Some(__e) => {
1758 let __cows = __e.components.iter()
1759 .map(|(s, _)| ::std::borrow::Cow::Borrowed(s.as_str()))
1760 .collect::<::std::vec::Vec<::std::borrow::Cow<'_, str>>>();
1761 ::core::option::Option::Some(
1762 <#inner_ty as ::edifact_rs::EdifactCompositeDeserialize>::edifact_deserialize_composite(
1763 ::edifact_rs::CompositeElement::from_slice(&__cows)
1764 )?
1765 )
1766 }
1767 ::core::option::Option::None => ::core::option::Option::None,
1768 };
1769 });
1770 }
1771 return Ok(quote! {
1772 let #ident = {
1773 let __cows = __seg.elements.get(#idx)
1774 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
1775 tag: #seg_tag.to_owned(),
1776 element_index: #idx,
1777 })?
1778 .components.iter()
1779 .map(|(s, _)| ::std::borrow::Cow::Borrowed(s.as_str()))
1780 .collect::<::std::vec::Vec<::std::borrow::Cow<'_, str>>>();
1781 <#ty as ::edifact_rs::EdifactCompositeDeserialize>::edifact_deserialize_composite(
1782 ::edifact_rs::CompositeElement::from_slice(&__cows)
1783 )?
1784 };
1785 });
1786 }
1787 let comp = &slot.component;
1788 let value_expr_owned = if slot.has_component {
1789 quote! { __seg.component_str(#idx, #comp) }
1790 } else {
1791 quote! { __seg.element_str(#idx) }
1792 };
1793 let names_component = &slot.names_component;
1796 let missing_required_err_owned = quote! {
1797 if #names_component {
1798 ::edifact_rs::EdifactError::MissingRequiredComponent {
1799 tag: #seg_tag.to_owned(),
1800 element_index: #idx,
1801 component_index: #comp,
1802 }
1803 } else {
1804 ::edifact_rs::EdifactError::MissingRequiredElement {
1805 tag: #seg_tag.to_owned(),
1806 element_index: #idx,
1807 }
1808 }
1809 };
1810 Ok(if is_option_type(ty) {
1811 if let Some(inner_ty) = option_inner_type(ty) {
1812 if attrs.required {
1813 if is_str_like(inner_ty) {
1817 quote! {
1818 let #ident = ::core::option::Option::Some(
1819 #value_expr_owned
1820 .filter(|__s| !__s.is_empty())
1821 .ok_or_else(|| #missing_required_err_owned)?
1822 .to_owned()
1823 );
1824 }
1825 } else {
1826 quote! {
1827 let #ident = ::core::option::Option::Some(
1828 #value_expr_owned
1829 .filter(|__s| !__s.is_empty())
1830 .ok_or_else(|| #missing_required_err_owned)?
1831 .parse::<#inner_ty>()
1832 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })?
1833 );
1834 }
1835 }
1836 } else if is_str_like(inner_ty) {
1837 quote! {
1838 let #ident = #value_expr_owned
1839 .filter(|__s| !__s.is_empty())
1840 .map(::std::string::String::from);
1841 }
1842 } else {
1843 quote! {
1844 let #ident = #value_expr_owned
1845 .filter(|__s| !__s.is_empty())
1846 .map(|__s| __s.parse::<#inner_ty>()
1847 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })
1848 )
1849 .transpose()?;
1850 }
1851 }
1852 } else {
1853 quote! {
1855 let #ident = #value_expr_owned
1856 .filter(|__s| !__s.is_empty())
1857 .map(::std::string::String::from);
1858 }
1859 }
1860 } else if is_str_like(ty) {
1861 quote! {
1862 let #ident = #value_expr_owned
1863 .filter(|__s| !__s.is_empty())
1864 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
1865 tag: #seg_tag.to_owned(),
1866 element_index: #idx,
1867 })?
1868 .to_owned();
1869 }
1870 } else {
1871 quote! {
1872 let #ident = #value_expr_owned
1873 .filter(|__s| !__s.is_empty())
1874 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
1875 tag: #seg_tag.to_owned(),
1876 element_index: #idx,
1877 })?
1878 .parse::<#ty>()
1879 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })?;
1880 }
1881 })
1882 })
1883 .collect::<syn::Result<_>>()?;
1884
1885 let owned_body = quote! {
1886 #slot_prelude
1887 let __seg = #find_seg_owned
1888 .ok_or_else(|| ::edifact_rs::EdifactError::MissingSegment {
1889 tag: #seg_tag.to_owned(),
1890 expected_position: "message body".to_owned(),
1891 })?;
1892 #qualifier_guard
1893 #(#field_inits_owned)*
1894 ::core::result::Result::Ok(Self { #(#field_names),* })
1895 };
1896
1897 (body, owned_body, seg_tag_impl)
1898 } else {
1899 let field_inits: Vec<TokenStream2> = field_data
1901 .iter()
1902 .map(|(ident, ty, attrs)| -> syn::Result<TokenStream2> {
1903 Ok(if let Some(qual) = &attrs.qualifier {
1904 if attrs.group || is_vec_type(ty) {
1905 let inner_ty = vec_inner_type(ty)
1906 .ok_or_else(|| syn::Error::new(ident.span(), "expected Vec<T>"))?;
1907 quote! {
1908 let #ident = segments
1909 .iter()
1910 .filter(|__seg| {
1911 __seg.tag == <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG
1912 && __seg.element_str(0).unwrap_or("") == #qual
1913 })
1914 .map(|__seg| {
1915 ::edifact_rs::EdifactDeserialize::edifact_deserialize(
1916 ::core::slice::from_ref(__seg),
1917 )
1918 })
1919 .collect::<::core::result::Result<::std::vec::Vec<#inner_ty>, ::edifact_rs::EdifactError>>()?;
1920 }
1921 } else if is_option_type(ty) {
1922 let inner_ty = option_inner_type(ty)
1923 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1924 quote! {
1925 let #ident = match ::edifact_rs::find_qualified_segment(
1926 segments,
1927 <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG,
1928 #qual,
1929 ) {
1930 ::core::option::Option::Some(__seg) => {
1931 ::core::option::Option::Some(
1932 ::edifact_rs::EdifactDeserialize::edifact_deserialize(
1933 ::core::slice::from_ref(__seg),
1934 )?
1935 )
1936 }
1937 ::core::option::Option::None => ::core::option::Option::None,
1938 };
1939 }
1940 } else {
1941 quote! {
1942 let __seg = ::edifact_rs::find_qualified_segment(
1943 segments,
1944 <#ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG,
1945 #qual,
1946 )
1947 .ok_or_else(|| ::edifact_rs::EdifactError::MissingSegment {
1948 tag: <#ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG.to_owned(),
1949 expected_position: "message body".to_owned(),
1950 })?;
1951 let #ident = ::edifact_rs::EdifactDeserialize::edifact_deserialize(
1952 ::core::slice::from_ref(__seg),
1953 )?;
1954 }
1955 }
1956 } else if attrs.group || is_vec_type(ty) {
1957 let inner_ty = vec_inner_type(ty)
1958 .ok_or_else(|| syn::Error::new(ident.span(), "expected Vec<T>"))?;
1959 quote! {
1960 let #ident = ::edifact_rs::find_segments_typed::<#inner_ty>(segments)
1961 .map(|__seg| {
1962 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize(
1963 ::core::slice::from_ref(__seg),
1964 )
1965 })
1966 .collect::<::core::result::Result<::std::vec::Vec<#inner_ty>, _>>()?;
1967 }
1968 } else if is_option_type(ty) {
1969 let inner_ty = option_inner_type(ty)
1970 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1971 quote! {
1972 let #ident = if segments
1973 .iter()
1974 .any(|__seg| __seg.tag == <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG)
1975 {
1976 ::core::option::Option::Some(
1977 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize(segments)?
1978 )
1979 } else {
1980 ::core::option::Option::None
1981 };
1982 }
1983 } else {
1984 quote! {
1985 let #ident = ::edifact_rs::EdifactDeserialize::edifact_deserialize(segments)?;
1986 }
1987 })
1988 })
1989 .collect::<syn::Result<_>>()?;
1990
1991 let body = quote! {
1992 #(#field_inits)*
1993 ::core::result::Result::Ok(Self { #(#field_names),* })
1994 };
1995
1996 let field_inits_owned: Vec<TokenStream2> = field_data
1999 .iter()
2000 .map(|(ident, ty, attrs)| -> syn::Result<TokenStream2> {
2001 Ok(if let Some(qual) = &attrs.qualifier {
2002 if attrs.group || is_vec_type(ty) {
2003 let inner_ty = vec_inner_type(ty)
2004 .ok_or_else(|| syn::Error::new(ident.span(), "expected Vec<T>"))?;
2005 quote! {
2006 let #ident = segments
2007 .iter()
2008 .filter(|__seg| {
2009 __seg.tag == <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG
2010 && __seg.element_str(0).unwrap_or("") == #qual
2011 })
2012 .map(|__seg| {
2013 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(
2014 ::core::slice::from_ref(__seg),
2015 )
2016 })
2017 .collect::<::core::result::Result<::std::vec::Vec<#inner_ty>, ::edifact_rs::EdifactError>>()?;
2018 }
2019 } else if is_option_type(ty) {
2020 let inner_ty = option_inner_type(ty)
2021 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
2022 quote! {
2023 let #ident = match ::edifact_rs::find_qualified_segment_owned(
2024 segments,
2025 <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG,
2026 #qual,
2027 ) {
2028 ::core::option::Option::Some(__seg) => {
2029 ::core::option::Option::Some(
2030 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(
2031 ::core::slice::from_ref(__seg),
2032 )?
2033 )
2034 }
2035 ::core::option::Option::None => ::core::option::Option::None,
2036 };
2037 }
2038 } else {
2039 quote! {
2040 let __seg = ::edifact_rs::find_qualified_segment_owned(
2041 segments,
2042 <#ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG,
2043 #qual,
2044 )
2045 .ok_or_else(|| ::edifact_rs::EdifactError::MissingSegment {
2046 tag: <#ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG.to_owned(),
2047 expected_position: "message body".to_owned(),
2048 })?;
2049 let #ident = <#ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(
2050 ::core::slice::from_ref(__seg),
2051 )?;
2052 }
2053 }
2054 } else if attrs.group || is_vec_type(ty) {
2055 let inner_ty = vec_inner_type(ty)
2056 .ok_or_else(|| syn::Error::new(ident.span(), "expected Vec<T>"))?;
2057 quote! {
2058 let #ident = segments
2059 .iter()
2060 .filter(|__seg| <#inner_ty as ::edifact_rs::EdifactSegmentTag>::matches_owned_segment(__seg))
2061 .map(|__seg| {
2062 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(
2063 ::core::slice::from_ref(__seg),
2064 )
2065 })
2066 .collect::<::core::result::Result<::std::vec::Vec<#inner_ty>, _>>()?;
2067 }
2068 } else if is_option_type(ty) {
2069 let inner_ty = option_inner_type(ty)
2070 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
2071 quote! {
2072 let #ident = if segments
2073 .iter()
2074 .any(|__seg| __seg.tag == <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG)
2075 {
2076 ::core::option::Option::Some(
2077 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(segments)?
2078 )
2079 } else {
2080 ::core::option::Option::None
2081 };
2082 }
2083 } else {
2084 quote! {
2085 let #ident = <#ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(segments)?;
2086 }
2087 })
2088 })
2089 .collect::<syn::Result<_>>()?;
2090
2091 let owned_body = quote! {
2092 #(#field_inits_owned)*
2093 ::core::result::Result::Ok(Self { #(#field_names),* })
2094 };
2095
2096 (body, owned_body, quote! {})
2097 };
2098
2099 Ok(quote! {
2100 impl ::edifact_rs::EdifactDeserialize for #name {
2101 fn edifact_deserialize(
2102 segments: &[::edifact_rs::Segment<'_>],
2103 ) -> ::core::result::Result<Self, ::edifact_rs::EdifactError> {
2104 #body
2105 }
2106
2107 fn edifact_deserialize_owned(
2108 segments: &[::edifact_rs::OwnedSegment],
2109 ) -> ::core::result::Result<Self, ::edifact_rs::EdifactError> {
2110 #owned_body
2111 }
2112 }
2113 #segment_tag_impl
2114 })
2115}
2116
2117#[cfg(test)]
2118mod tests {
2119 #[test]
2133 fn trybuild_ui() {
2134 if std::env::var_os("EDIFACT_UI_TESTS").is_none() {
2135 eprintln!(
2136 "skipping derive UI suite: set EDIFACT_UI_TESTS=1 to run it \
2137 (expectations are pinned to the MSRV toolchain)"
2138 );
2139 return;
2140 }
2141 let t = trybuild::TestCases::new();
2142 t.pass("tests/ui/pass_*.rs");
2143 t.compile_fail("tests/ui/fail_*.rs");
2144 }
2145}