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 let (qualifier_emit, start_slot, elem0_comp_stmts) = if let Some(qual) =
1225 &struct_attrs.qualifier
1226 {
1227 for (i, (ident, _, attrs)) in field_data.iter().enumerate() {
1229 let elem = static_element_index(attrs, i);
1230 let comp = attrs.component.unwrap_or(0);
1231 if elem == 0 && comp == 0 {
1232 return Err(syn::Error::new(
1233 attrs
1234 .element_span
1235 .or(attrs.component_span)
1236 .unwrap_or_else(|| ident.span()),
1237 format!(
1238 "field `{}`: cannot use #[edifact(qualifier = ...)] with a field at element = 0 without component >= 1; the qualifier occupies component 0",
1239 ident
1240 ),
1241 ));
1242 }
1243 }
1244 let mut comp_fields: Vec<(u32, usize)> = field_data
1246 .iter()
1247 .enumerate()
1248 .filter_map(|(i, (_, _, attrs))| {
1249 let elem = static_element_index(attrs, i);
1250 let comp = attrs.component.unwrap_or(0);
1251 if elem == 0 && comp > 0 {
1252 Some((comp, i))
1253 } else {
1254 None
1255 }
1256 })
1257 .collect();
1258 comp_fields.sort_by_key(|(c, _)| *c);
1259 let comp_stmts: Vec<TokenStream2> = comp_fields
1260 .iter()
1261 .map(|(_, fi)| {
1262 let (ident, ty, _) = &field_data[*fi];
1263 emit_component_element(ident, ty)
1264 })
1265 .collect();
1266 let q = quote! {
1267 emitter.emit(::edifact_rs::EdifactEvent::Element { value: #qual })?;
1268 };
1269 (q, 1u32, quote! { #(#comp_stmts)* })
1270 } else {
1271 (quote! {}, 0u32, quote! {})
1272 };
1273
1274 let regular_field_data: Vec<(u32, usize)> = field_data
1276 .iter()
1277 .enumerate()
1278 .filter_map(|(i, (_, _, attrs))| {
1279 let elem = static_element_index(attrs, i);
1280 if elem < start_slot {
1281 None
1282 } else {
1283 Some((elem, i))
1284 }
1285 })
1286 .collect();
1287 let reg_max_idx = regular_field_data
1288 .iter()
1289 .map(|(e, _)| *e)
1290 .max()
1291 .unwrap_or(start_slot.saturating_sub(1));
1292 let reg_field_map: std::collections::HashMap<u32, usize> =
1293 regular_field_data.iter().copied().collect();
1294
1295 let mut elem_stmts: Vec<TokenStream2> = Vec::new();
1296 for slot in start_slot..=reg_max_idx {
1297 if let Some(&fi) = reg_field_map.get(&slot) {
1298 let (ident, ty, attrs) = &field_data[fi];
1299 if attrs.composite {
1300 elem_stmts.push(emit_composite_field(ident, ty));
1301 } else {
1302 elem_stmts.push(emit_element(ident, ty));
1303 }
1304 } else {
1305 elem_stmts.push(quote! {
1307 emitter.emit(::edifact_rs::EdifactEvent::Element { value: "" })?;
1308 });
1309 }
1310 }
1311
1312 quote! {
1313 emitter.emit(::edifact_rs::EdifactEvent::StartSegment { tag: #seg_tag })?;
1314 #qualifier_emit
1315 #elem0_comp_stmts
1316 #(#elem_stmts)*
1317 emitter.emit(::edifact_rs::EdifactEvent::EndSegment)?;
1318 }
1319 } else {
1320 let stmts: Vec<TokenStream2> = field_data
1322 .iter()
1323 .map(|(ident, ty, attrs)| {
1324 if attrs.group || is_vec_type(ty) {
1325 quote! {
1326 for __item in &self.#ident {
1327 ::edifact_rs::EdifactSerialize::edifact_serialize(__item, emitter)?;
1328 }
1329 }
1330 } else {
1331 quote! {
1332 ::edifact_rs::EdifactSerialize::edifact_serialize(&self.#ident, emitter)?;
1333 }
1334 }
1335 })
1336 .collect();
1337 quote! { #(#stmts)* }
1338 };
1339
1340 Ok(quote! {
1341 impl ::edifact_rs::EdifactSerialize for #name {
1342 fn edifact_serialize<__E: ::edifact_rs::EventEmitter>(
1343 &self,
1344 emitter: &mut __E,
1345 ) -> ::core::result::Result<(), ::edifact_rs::EdifactError> {
1346 #body
1347 ::core::result::Result::Ok(())
1348 }
1349 }
1350 })
1351}
1352
1353fn emit_element(ident: &syn::Ident, ty: &Type) -> TokenStream2 {
1358 if is_option_type(ty) {
1359 let inner_is_str = option_inner_type(ty).is_some_and(is_str_like);
1360 if inner_is_str {
1361 quote! {
1362 match &self.#ident {
1363 ::core::option::Option::Some(__v) => {
1364 emitter.emit(::edifact_rs::EdifactEvent::Element { value: __v.as_str() })?;
1365 }
1366 ::core::option::Option::None => {
1367 emitter.emit(::edifact_rs::EdifactEvent::Element { value: "" })?;
1368 }
1369 }
1370 }
1371 } else {
1372 quote! {
1373 match &self.#ident {
1374 ::core::option::Option::Some(__v) => {
1375 let __s = ::std::string::ToString::to_string(__v);
1376 emitter.emit(::edifact_rs::EdifactEvent::Element { value: &__s })?;
1377 }
1378 ::core::option::Option::None => {
1379 emitter.emit(::edifact_rs::EdifactEvent::Element { value: "" })?;
1380 }
1381 }
1382 }
1383 }
1384 } else if is_string_type(ty) {
1385 quote! {
1386 emitter.emit(::edifact_rs::EdifactEvent::Element { value: self.#ident.as_str() })?;
1387 }
1388 } else if is_str_ref_type(ty) {
1389 quote! {
1390 emitter.emit(::edifact_rs::EdifactEvent::Element { value: self.#ident })?;
1391 }
1392 } else {
1393 quote! {
1394 {
1395 let __s = ::std::string::ToString::to_string(&self.#ident);
1396 emitter.emit(::edifact_rs::EdifactEvent::Element { value: &__s })?;
1397 }
1398 }
1399 }
1400}
1401
1402fn emit_component_element(ident: &syn::Ident, ty: &Type) -> TokenStream2 {
1406 if is_option_type(ty) {
1407 let inner_is_str = option_inner_type(ty).is_some_and(is_str_like);
1408 if inner_is_str {
1409 quote! {
1410 match &self.#ident {
1411 ::core::option::Option::Some(__v) => {
1412 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: __v.as_str() })?;
1413 }
1414 ::core::option::Option::None => {
1415 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: "" })?;
1416 }
1417 }
1418 }
1419 } else {
1420 quote! {
1421 match &self.#ident {
1422 ::core::option::Option::Some(__v) => {
1423 let __s = ::std::string::ToString::to_string(__v);
1424 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: &__s })?;
1425 }
1426 ::core::option::Option::None => {
1427 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: "" })?;
1428 }
1429 }
1430 }
1431 }
1432 } else if is_string_type(ty) {
1433 quote! {
1434 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: self.#ident.as_str() })?;
1435 }
1436 } else if is_str_ref_type(ty) {
1437 quote! {
1438 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: self.#ident })?;
1439 }
1440 } else {
1441 quote! {
1442 {
1443 let __s = ::std::string::ToString::to_string(&self.#ident);
1444 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: &__s })?;
1445 }
1446 }
1447 }
1448}
1449
1450fn emit_composite_field(ident: &syn::Ident, ty: &Type) -> TokenStream2 {
1452 if is_option_type(ty) {
1453 quote! {
1454 match &self.#ident {
1455 ::core::option::Option::Some(__v) => {
1456 ::edifact_rs::EdifactCompositeSerialize::edifact_serialize_composite(__v, emitter)?;
1457 }
1458 ::core::option::Option::None => {
1459 emitter.emit(::edifact_rs::EdifactEvent::Element { value: "" })?;
1460 }
1461 }
1462 }
1463 } else {
1464 quote! {
1465 ::edifact_rs::EdifactCompositeSerialize::edifact_serialize_composite(&self.#ident, emitter)?;
1466 }
1467 }
1468}
1469
1470fn impl_deserialize(input: &DeriveInput) -> syn::Result<TokenStream2> {
1473 let name = &input.ident;
1474 let struct_attrs = parse_struct_attrs(input)?;
1475 let fields = get_named_fields(input)?;
1476 let is_segment_struct = struct_attrs.segment.is_some();
1477
1478 let field_data: Vec<(&syn::Ident, &Type, FieldAttrs)> = fields
1479 .named
1480 .iter()
1481 .map(|f| {
1482 let attrs = parse_field_attrs(f)?;
1483 let ident = f
1484 .ident
1485 .as_ref()
1486 .ok_or_else(|| syn::Error::new_spanned(f, "only named fields are supported"))?;
1487 validate_field_attrs(ident, &f.ty, &attrs, is_segment_struct)?;
1488 Ok((ident, &f.ty, attrs))
1489 })
1490 .collect::<syn::Result<_>>()?;
1491 check_duplicate_slots(&field_data, is_segment_struct)?;
1492 let (slot_prelude, slots) = resolve_slots(&struct_attrs, &field_data)?;
1493
1494 let field_names: Vec<&syn::Ident> = field_data.iter().map(|(id, _, _)| *id).collect();
1495
1496 let (body, owned_body, segment_tag_impl) = if let Some(seg_tag) = &struct_attrs.segment {
1497 let qualifier_guard = if let Some(qual) = &struct_attrs.qualifier {
1499 quote! {
1500 if __seg.element_str(0).unwrap_or("") != #qual {
1501 return ::core::result::Result::Err(
1502 ::edifact_rs::EdifactError::MissingRequiredElement {
1503 tag: #seg_tag.to_owned(),
1504 element_index: 0,
1505 }
1506 );
1507 }
1508 }
1509 } else if let Some(idx) = struct_attrs.qualifier_from {
1510 quote! {
1511 match __seg.element_str(#idx as usize) {
1515 ::core::option::Option::None => return ::core::result::Result::Err(
1516 ::edifact_rs::EdifactError::MissingRequiredElement {
1517 tag: #seg_tag.to_owned(),
1518 element_index: #idx as usize,
1519 }
1520 ),
1521 ::core::option::Option::Some("") => return ::core::result::Result::Err(
1522 ::edifact_rs::EdifactError::InvalidFieldValue {
1523 tag: #seg_tag.to_owned(),
1524 element_index: #idx as usize,
1525 value: ::std::string::String::new(),
1526 }
1527 ),
1528 ::core::option::Option::Some(__qual_val) => { let _ = __qual_val; }
1529 }
1530 }
1531 } else {
1532 quote! {}
1533 };
1534
1535 let find_seg = if let Some(qual) = &struct_attrs.qualifier {
1536 quote! {
1537 ::edifact_rs::find_qualified_segment(segments, #seg_tag, #qual)
1538 }
1539 } else {
1540 quote! {
1541 ::edifact_rs::find_segment(segments, #seg_tag)
1542 }
1543 };
1544
1545 let field_inits: Vec<TokenStream2> = field_data
1546 .iter()
1547 .zip(slots.iter())
1548 .map(|((ident, ty, attrs), slot)| -> syn::Result<TokenStream2> {
1549 let idx = &slot.element;
1550 if attrs.composite {
1551 if is_option_type(ty) {
1552 let inner_ty = option_inner_type(ty)
1553 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1554 return Ok(quote! {
1555 let #ident = match ::edifact_rs::composite_element(__seg, #idx) {
1556 ::core::option::Option::Some(__composite) => {
1557 ::core::option::Option::Some(
1558 <#inner_ty as ::edifact_rs::EdifactCompositeDeserialize>::edifact_deserialize_composite(__composite)?
1559 )
1560 }
1561 ::core::option::Option::None => ::core::option::Option::None,
1562 };
1563 });
1564 }
1565 return Ok(quote! {
1566 let #ident = <#ty as ::edifact_rs::EdifactCompositeDeserialize>::edifact_deserialize_composite(
1567 ::edifact_rs::composite_element(__seg, #idx).ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
1568 tag: #seg_tag.to_owned(),
1569 element_index: #idx,
1570 })?
1571 )?;
1572 });
1573 }
1574 let comp = &slot.component;
1575 let value_expr = if slot.has_component {
1576 quote! {
1577 __seg.get_element(#idx).and_then(|__e| __e.get_component(#comp))
1578 }
1579 } else {
1580 quote! { __seg.element_str(#idx) }
1581 };
1582 let names_component = &slot.names_component;
1586 let missing_required_err = quote! {
1587 if #names_component {
1588 ::edifact_rs::EdifactError::MissingRequiredComponent {
1589 tag: #seg_tag.to_owned(),
1590 element_index: #idx,
1591 component_index: #comp,
1592 }
1593 } else {
1594 ::edifact_rs::EdifactError::MissingRequiredElement {
1595 tag: #seg_tag.to_owned(),
1596 element_index: #idx,
1597 }
1598 }
1599 };
1600 Ok(if is_option_type(ty) {
1601 let inner_ty = option_inner_type(ty);
1602 let inner_is_str = inner_ty.is_some_and(is_str_like);
1603 if attrs.required {
1604 if inner_is_str {
1608 quote! {
1609 let #ident = ::core::option::Option::Some(
1610 #value_expr
1611 .filter(|__s| !__s.is_empty())
1612 .ok_or_else(|| #missing_required_err)?
1613 .to_owned()
1614 );
1615 }
1616 } else {
1617 let inner_ty = inner_ty
1618 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1619 quote! {
1620 let #ident = ::core::option::Option::Some(
1621 #value_expr
1622 .filter(|__s| !__s.is_empty())
1623 .ok_or_else(|| #missing_required_err)?
1624 .parse::<#inner_ty>()
1625 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })?
1626 );
1627 }
1628 }
1629 } else if inner_is_str {
1630 quote! {
1631 let #ident = #value_expr
1632 .filter(|__s| !__s.is_empty())
1633 .map(::std::string::String::from);
1634 }
1635 } else {
1636 let inner_ty = inner_ty
1637 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1638 quote! {
1639 let #ident = #value_expr
1640 .filter(|__s| !__s.is_empty())
1641 .map(|__s| __s.parse::<#inner_ty>()
1642 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })
1643 )
1644 .transpose()?;
1645 }
1646 }
1647 } else if is_str_like(ty) {
1648 quote! {
1649 let #ident = #value_expr
1650 .filter(|__s| !__s.is_empty())
1651 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
1652 tag: #seg_tag.to_owned(),
1653 element_index: #idx,
1654 })?
1655 .to_owned();
1656 }
1657 } else {
1658 quote! {
1659 let #ident = #value_expr
1660 .filter(|__s| !__s.is_empty())
1661 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
1662 tag: #seg_tag.to_owned(),
1663 element_index: #idx,
1664 })?
1665 .parse::<#ty>()
1666 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })?;
1667 }
1668 })
1669 })
1670 .collect::<syn::Result<_>>()?;
1671
1672 let body = quote! {
1673 #slot_prelude
1674 let __seg = #find_seg
1675 .ok_or_else(|| ::edifact_rs::EdifactError::MissingSegment {
1676 tag: #seg_tag.to_owned(),
1677 expected_position: "message body".to_owned(),
1678 })?;
1679 #qualifier_guard
1680 #(#field_inits)*
1681 ::core::result::Result::Ok(Self { #(#field_names),* })
1682 };
1683
1684 let qualifier_match = if let Some(qual) = &struct_attrs.qualifier {
1691 quote! {
1692 const QUALIFIER_PATTERN: ::core::option::Option<&'static str> =
1693 ::core::option::Option::Some(#qual);
1694 }
1695 } else if let Some(idx) = struct_attrs.qualifier_from {
1696 quote! {
1700 fn matches_segment(seg: &::edifact_rs::Segment<'_>) -> bool {
1701 seg.tag == Self::SEGMENT_TAG
1702 && !seg.element_str(#idx as usize).unwrap_or("").is_empty()
1703 }
1704
1705 fn matches_owned_segment(seg: &::edifact_rs::OwnedSegment) -> bool {
1706 seg.tag == Self::SEGMENT_TAG
1707 && !seg
1708 .elements
1709 .get(#idx as usize)
1710 .and_then(|e| e.components.first())
1711 .map(|(c, _)| c.as_str())
1712 .unwrap_or("")
1713 .is_empty()
1714 }
1715 }
1716 } else {
1717 quote! {}
1718 };
1719
1720 let seg_tag_impl = quote! {
1721 impl ::edifact_rs::EdifactSegmentTag for #name {
1722 const SEGMENT_TAG: &'static str = #seg_tag;
1723 #qualifier_match
1724 }
1725 };
1726
1727 let find_seg_owned = if let Some(qual) = &struct_attrs.qualifier {
1730 quote! {
1731 ::edifact_rs::find_qualified_segment_owned(segments, #seg_tag, #qual)
1732 }
1733 } else {
1734 quote! {
1735 ::edifact_rs::find_segment_owned(segments, #seg_tag)
1736 }
1737 };
1738
1739 let field_inits_owned: Vec<TokenStream2> = field_data
1740 .iter()
1741 .zip(slots.iter())
1742 .map(|((ident, ty, attrs), slot)| -> syn::Result<TokenStream2> {
1743 let idx = &slot.element;
1744 if attrs.composite {
1745 if is_option_type(ty) {
1746 let inner_ty = option_inner_type(ty)
1747 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1748 return Ok(quote! {
1749 let #ident = match __seg.elements.get(#idx) {
1750 ::core::option::Option::Some(__e) => {
1751 let __cows = __e.components.iter()
1752 .map(|(s, _)| ::std::borrow::Cow::Borrowed(s.as_str()))
1753 .collect::<::std::vec::Vec<::std::borrow::Cow<'_, str>>>();
1754 ::core::option::Option::Some(
1755 <#inner_ty as ::edifact_rs::EdifactCompositeDeserialize>::edifact_deserialize_composite(
1756 ::edifact_rs::CompositeElement::from_slice(&__cows)
1757 )?
1758 )
1759 }
1760 ::core::option::Option::None => ::core::option::Option::None,
1761 };
1762 });
1763 }
1764 return Ok(quote! {
1765 let #ident = {
1766 let __cows = __seg.elements.get(#idx)
1767 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
1768 tag: #seg_tag.to_owned(),
1769 element_index: #idx,
1770 })?
1771 .components.iter()
1772 .map(|(s, _)| ::std::borrow::Cow::Borrowed(s.as_str()))
1773 .collect::<::std::vec::Vec<::std::borrow::Cow<'_, str>>>();
1774 <#ty as ::edifact_rs::EdifactCompositeDeserialize>::edifact_deserialize_composite(
1775 ::edifact_rs::CompositeElement::from_slice(&__cows)
1776 )?
1777 };
1778 });
1779 }
1780 let comp = &slot.component;
1781 let value_expr_owned = if slot.has_component {
1782 quote! { __seg.component_str(#idx, #comp) }
1783 } else {
1784 quote! { __seg.element_str(#idx) }
1785 };
1786 let names_component = &slot.names_component;
1789 let missing_required_err_owned = quote! {
1790 if #names_component {
1791 ::edifact_rs::EdifactError::MissingRequiredComponent {
1792 tag: #seg_tag.to_owned(),
1793 element_index: #idx,
1794 component_index: #comp,
1795 }
1796 } else {
1797 ::edifact_rs::EdifactError::MissingRequiredElement {
1798 tag: #seg_tag.to_owned(),
1799 element_index: #idx,
1800 }
1801 }
1802 };
1803 Ok(if is_option_type(ty) {
1804 if let Some(inner_ty) = option_inner_type(ty) {
1805 if attrs.required {
1806 if is_str_like(inner_ty) {
1810 quote! {
1811 let #ident = ::core::option::Option::Some(
1812 #value_expr_owned
1813 .filter(|__s| !__s.is_empty())
1814 .ok_or_else(|| #missing_required_err_owned)?
1815 .to_owned()
1816 );
1817 }
1818 } else {
1819 quote! {
1820 let #ident = ::core::option::Option::Some(
1821 #value_expr_owned
1822 .filter(|__s| !__s.is_empty())
1823 .ok_or_else(|| #missing_required_err_owned)?
1824 .parse::<#inner_ty>()
1825 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })?
1826 );
1827 }
1828 }
1829 } else if is_str_like(inner_ty) {
1830 quote! {
1831 let #ident = #value_expr_owned
1832 .filter(|__s| !__s.is_empty())
1833 .map(::std::string::String::from);
1834 }
1835 } else {
1836 quote! {
1837 let #ident = #value_expr_owned
1838 .filter(|__s| !__s.is_empty())
1839 .map(|__s| __s.parse::<#inner_ty>()
1840 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })
1841 )
1842 .transpose()?;
1843 }
1844 }
1845 } else {
1846 quote! {
1848 let #ident = #value_expr_owned
1849 .filter(|__s| !__s.is_empty())
1850 .map(::std::string::String::from);
1851 }
1852 }
1853 } else if is_str_like(ty) {
1854 quote! {
1855 let #ident = #value_expr_owned
1856 .filter(|__s| !__s.is_empty())
1857 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
1858 tag: #seg_tag.to_owned(),
1859 element_index: #idx,
1860 })?
1861 .to_owned();
1862 }
1863 } else {
1864 quote! {
1865 let #ident = #value_expr_owned
1866 .filter(|__s| !__s.is_empty())
1867 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
1868 tag: #seg_tag.to_owned(),
1869 element_index: #idx,
1870 })?
1871 .parse::<#ty>()
1872 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })?;
1873 }
1874 })
1875 })
1876 .collect::<syn::Result<_>>()?;
1877
1878 let owned_body = quote! {
1879 #slot_prelude
1880 let __seg = #find_seg_owned
1881 .ok_or_else(|| ::edifact_rs::EdifactError::MissingSegment {
1882 tag: #seg_tag.to_owned(),
1883 expected_position: "message body".to_owned(),
1884 })?;
1885 #qualifier_guard
1886 #(#field_inits_owned)*
1887 ::core::result::Result::Ok(Self { #(#field_names),* })
1888 };
1889
1890 (body, owned_body, seg_tag_impl)
1891 } else {
1892 let field_inits: Vec<TokenStream2> = field_data
1894 .iter()
1895 .map(|(ident, ty, attrs)| -> syn::Result<TokenStream2> {
1896 Ok(if let Some(qual) = &attrs.qualifier {
1897 if attrs.group || is_vec_type(ty) {
1898 let inner_ty = vec_inner_type(ty)
1899 .ok_or_else(|| syn::Error::new(ident.span(), "expected Vec<T>"))?;
1900 quote! {
1901 let #ident = segments
1902 .iter()
1903 .filter(|__seg| {
1904 __seg.tag == <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG
1905 && __seg.element_str(0).unwrap_or("") == #qual
1906 })
1907 .map(|__seg| {
1908 ::edifact_rs::EdifactDeserialize::edifact_deserialize(
1909 ::core::slice::from_ref(__seg),
1910 )
1911 })
1912 .collect::<::core::result::Result<::std::vec::Vec<#inner_ty>, ::edifact_rs::EdifactError>>()?;
1913 }
1914 } else if is_option_type(ty) {
1915 let inner_ty = option_inner_type(ty)
1916 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1917 quote! {
1918 let #ident = match ::edifact_rs::find_qualified_segment(
1919 segments,
1920 <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG,
1921 #qual,
1922 ) {
1923 ::core::option::Option::Some(__seg) => {
1924 ::core::option::Option::Some(
1925 ::edifact_rs::EdifactDeserialize::edifact_deserialize(
1926 ::core::slice::from_ref(__seg),
1927 )?
1928 )
1929 }
1930 ::core::option::Option::None => ::core::option::Option::None,
1931 };
1932 }
1933 } else {
1934 quote! {
1935 let __seg = ::edifact_rs::find_qualified_segment(
1936 segments,
1937 <#ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG,
1938 #qual,
1939 )
1940 .ok_or_else(|| ::edifact_rs::EdifactError::MissingSegment {
1941 tag: <#ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG.to_owned(),
1942 expected_position: "message body".to_owned(),
1943 })?;
1944 let #ident = ::edifact_rs::EdifactDeserialize::edifact_deserialize(
1945 ::core::slice::from_ref(__seg),
1946 )?;
1947 }
1948 }
1949 } else if attrs.group || is_vec_type(ty) {
1950 let inner_ty = vec_inner_type(ty)
1951 .ok_or_else(|| syn::Error::new(ident.span(), "expected Vec<T>"))?;
1952 quote! {
1953 let #ident = ::edifact_rs::find_segments_typed::<#inner_ty>(segments)
1954 .map(|__seg| {
1955 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize(
1956 ::core::slice::from_ref(__seg),
1957 )
1958 })
1959 .collect::<::core::result::Result<::std::vec::Vec<#inner_ty>, _>>()?;
1960 }
1961 } else if is_option_type(ty) {
1962 let inner_ty = option_inner_type(ty)
1963 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1964 quote! {
1965 let #ident = if segments
1966 .iter()
1967 .any(|__seg| __seg.tag == <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG)
1968 {
1969 ::core::option::Option::Some(
1970 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize(segments)?
1971 )
1972 } else {
1973 ::core::option::Option::None
1974 };
1975 }
1976 } else {
1977 quote! {
1978 let #ident = ::edifact_rs::EdifactDeserialize::edifact_deserialize(segments)?;
1979 }
1980 })
1981 })
1982 .collect::<syn::Result<_>>()?;
1983
1984 let body = quote! {
1985 #(#field_inits)*
1986 ::core::result::Result::Ok(Self { #(#field_names),* })
1987 };
1988
1989 let field_inits_owned: Vec<TokenStream2> = field_data
1992 .iter()
1993 .map(|(ident, ty, attrs)| -> syn::Result<TokenStream2> {
1994 Ok(if let Some(qual) = &attrs.qualifier {
1995 if attrs.group || is_vec_type(ty) {
1996 let inner_ty = vec_inner_type(ty)
1997 .ok_or_else(|| syn::Error::new(ident.span(), "expected Vec<T>"))?;
1998 quote! {
1999 let #ident = segments
2000 .iter()
2001 .filter(|__seg| {
2002 __seg.tag == <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG
2003 && __seg.element_str(0).unwrap_or("") == #qual
2004 })
2005 .map(|__seg| {
2006 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(
2007 ::core::slice::from_ref(__seg),
2008 )
2009 })
2010 .collect::<::core::result::Result<::std::vec::Vec<#inner_ty>, ::edifact_rs::EdifactError>>()?;
2011 }
2012 } else if is_option_type(ty) {
2013 let inner_ty = option_inner_type(ty)
2014 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
2015 quote! {
2016 let #ident = match ::edifact_rs::find_qualified_segment_owned(
2017 segments,
2018 <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG,
2019 #qual,
2020 ) {
2021 ::core::option::Option::Some(__seg) => {
2022 ::core::option::Option::Some(
2023 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(
2024 ::core::slice::from_ref(__seg),
2025 )?
2026 )
2027 }
2028 ::core::option::Option::None => ::core::option::Option::None,
2029 };
2030 }
2031 } else {
2032 quote! {
2033 let __seg = ::edifact_rs::find_qualified_segment_owned(
2034 segments,
2035 <#ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG,
2036 #qual,
2037 )
2038 .ok_or_else(|| ::edifact_rs::EdifactError::MissingSegment {
2039 tag: <#ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG.to_owned(),
2040 expected_position: "message body".to_owned(),
2041 })?;
2042 let #ident = <#ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(
2043 ::core::slice::from_ref(__seg),
2044 )?;
2045 }
2046 }
2047 } else if attrs.group || is_vec_type(ty) {
2048 let inner_ty = vec_inner_type(ty)
2049 .ok_or_else(|| syn::Error::new(ident.span(), "expected Vec<T>"))?;
2050 quote! {
2051 let #ident = segments
2052 .iter()
2053 .filter(|__seg| <#inner_ty as ::edifact_rs::EdifactSegmentTag>::matches_owned_segment(__seg))
2054 .map(|__seg| {
2055 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(
2056 ::core::slice::from_ref(__seg),
2057 )
2058 })
2059 .collect::<::core::result::Result<::std::vec::Vec<#inner_ty>, _>>()?;
2060 }
2061 } else if is_option_type(ty) {
2062 let inner_ty = option_inner_type(ty)
2063 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
2064 quote! {
2065 let #ident = if segments
2066 .iter()
2067 .any(|__seg| __seg.tag == <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG)
2068 {
2069 ::core::option::Option::Some(
2070 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(segments)?
2071 )
2072 } else {
2073 ::core::option::Option::None
2074 };
2075 }
2076 } else {
2077 quote! {
2078 let #ident = <#ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(segments)?;
2079 }
2080 })
2081 })
2082 .collect::<syn::Result<_>>()?;
2083
2084 let owned_body = quote! {
2085 #(#field_inits_owned)*
2086 ::core::result::Result::Ok(Self { #(#field_names),* })
2087 };
2088
2089 (body, owned_body, quote! {})
2090 };
2091
2092 Ok(quote! {
2093 impl ::edifact_rs::EdifactDeserialize for #name {
2094 fn edifact_deserialize(
2095 segments: &[::edifact_rs::Segment<'_>],
2096 ) -> ::core::result::Result<Self, ::edifact_rs::EdifactError> {
2097 #body
2098 }
2099
2100 fn edifact_deserialize_owned(
2101 segments: &[::edifact_rs::OwnedSegment],
2102 ) -> ::core::result::Result<Self, ::edifact_rs::EdifactError> {
2103 #owned_body
2104 }
2105 }
2106 #segment_tag_impl
2107 })
2108}
2109
2110#[cfg(test)]
2111mod tests {
2112 #[test]
2126 fn trybuild_ui() {
2127 if std::env::var_os("EDIFACT_UI_TESTS").is_none() {
2128 eprintln!(
2129 "skipping derive UI suite: set EDIFACT_UI_TESTS=1 to run it \
2130 (expectations are pinned to the MSRV toolchain)"
2131 );
2132 return;
2133 }
2134 let t = trybuild::TestCases::new();
2135 t.pass("tests/ui/pass_*.rs");
2136 t.compile_fail("tests/ui/fail_*.rs");
2137 }
2138}