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#[derive(Clone)]
148enum Position {
149 Index(u32),
151 Code(String),
153}
154
155#[derive(Default)]
156struct StructAttrs {
157 segment: Option<String>,
159 qualifier: Option<String>,
161 qualifier_span: Option<proc_macro2::Span>,
162 qualifier_from: Option<u32>,
164 qualifier_from_span: Option<proc_macro2::Span>,
165 layout: Option<syn::Path>,
168 layout_span: Option<proc_macro2::Span>,
169}
170
171#[derive(Default)]
172struct FieldAttrs {
173 element: Option<Position>,
175 element_span: Option<proc_macro2::Span>,
176 component: Option<u32>,
178 component_span: Option<proc_macro2::Span>,
179 composite: bool,
181 composite_span: Option<proc_macro2::Span>,
182 group: bool,
184 group_span: Option<proc_macro2::Span>,
185 qualifier: Option<String>,
187 qualifier_span: Option<proc_macro2::Span>,
188 required: bool,
196 required_span: Option<proc_macro2::Span>,
197}
198
199const MAX_POSITION_INDEX: u32 = 256;
209
210fn check_index_bound(lit: &syn::LitInt, value: u32, key: &str) -> syn::Result<()> {
211 if value > MAX_POSITION_INDEX {
212 return Err(syn::Error::new(
213 lit.span(),
214 format!(
215 "`{key}` index {value} exceeds the maximum of {MAX_POSITION_INDEX}; \
216 UN/EDIFACT allows at most 99 elements per segment and 99 components per composite"
217 ),
218 ));
219 }
220 Ok(())
221}
222
223fn parse_struct_attrs(input: &DeriveInput) -> syn::Result<StructAttrs> {
224 let mut out = StructAttrs::default();
225 for attr in &input.attrs {
226 if !attr.path().is_ident("edifact") {
227 continue;
228 }
229 attr.parse_nested_meta(|meta| {
230 if meta.path.is_ident("segment") {
231 if out.segment.is_some() {
232 return Err(meta.error("duplicate `segment`"));
233 }
234 let lit = meta.value()?.parse::<syn::LitStr>()?;
235 let tag = lit.value();
236 if tag.len() != 3 || !tag.bytes().all(|b| b.is_ascii_uppercase()) {
237 return Err(syn::Error::new(
238 lit.span(),
239 format!(
240 "segment tag must be exactly 3 ASCII uppercase letters; got {tag:?}"
241 ),
242 ));
243 }
244 out.segment = Some(tag);
245 } else if meta.path.is_ident("qualifier") {
246 if out.qualifier.is_some() {
247 return Err(meta.error("duplicate `qualifier`"));
248 }
249 out.qualifier = Some(meta.value()?.parse::<syn::LitStr>()?.value());
250 out.qualifier_span = Some(meta.path.span());
251 } else if meta.path.is_ident("qualifier_from") {
252 if out.qualifier_from.is_some() {
253 return Err(meta.error("duplicate `qualifier_from`"));
254 }
255 let lit = meta.value()?.parse::<syn::LitInt>()?;
256 let idx: u32 = lit.base10_parse()?;
257 check_index_bound(&lit, idx, "qualifier_from")?;
258 out.qualifier_from = Some(idx);
259 out.qualifier_from_span = Some(meta.path.span());
260 } else if meta.path.is_ident("layout") {
261 if out.layout.is_some() {
262 return Err(meta.error("duplicate `layout`"));
263 }
264 out.layout_span = Some(meta.path.span());
265 let value = meta.value()?;
266 out.layout = Some(if value.peek(syn::LitStr) {
270 value.parse::<syn::LitStr>()?.parse()?
271 } else {
272 value.parse::<syn::Path>()?
273 });
274 } else {
275 return Err(meta.error("unknown struct-level `edifact` key; expected `segment`, `qualifier`, `qualifier_from`, or `layout`"));
276 }
277 Ok(())
278 })?;
279 }
280 if out.layout.is_some() && out.segment.is_none() {
281 return Err(syn::Error::new(
282 out.layout_span.unwrap_or_else(|| input.span()),
283 "#[edifact(layout = ...)] requires #[edifact(segment = ...)]: a layout describes one segment",
284 ));
285 }
286 if (out.qualifier.is_some() || out.qualifier_from.is_some()) && out.segment.is_none() {
287 return Err(syn::Error::new(
288 out.qualifier_span
289 .or(out.qualifier_from_span)
290 .unwrap_or_else(|| input.span()),
291 "#[edifact(qualifier = ...)] / #[edifact(qualifier_from = ...)] require #[edifact(segment = ...)]",
292 ));
293 }
294 if out.qualifier.is_some() && out.qualifier_from.is_some() {
295 return Err(syn::Error::new(
296 out.qualifier_from_span
297 .or(out.qualifier_span)
298 .unwrap_or_else(|| input.span()),
299 "use either #[edifact(qualifier = ...)] or #[edifact(qualifier_from = ...)], not both",
300 ));
301 }
302 Ok(out)
303}
304
305fn parse_field_attrs(field: &Field) -> syn::Result<FieldAttrs> {
306 let mut out = FieldAttrs::default();
307 for attr in &field.attrs {
308 if !attr.path().is_ident("edifact") {
309 continue;
310 }
311 attr.parse_nested_meta(|meta| {
312 if meta.path.is_ident("element") {
313 if out.element.is_some() {
314 return Err(meta.error("duplicate `element`"));
315 }
316 out.element_span = Some(meta.path.span());
317 let value = meta.value()?;
318 out.element = Some(if value.peek(syn::LitStr) {
319 let lit = value.parse::<syn::LitStr>()?;
320 let code = lit.value();
321 if code.is_empty() {
322 return Err(syn::Error::new(
323 lit.span(),
324 "`element` data element identifier must not be empty",
325 ));
326 }
327 Position::Code(code)
328 } else {
329 let lit = value.parse::<syn::LitInt>()?;
330 let idx: u32 = lit.base10_parse()?;
331 check_index_bound(&lit, idx, "element")?;
332 Position::Index(idx)
333 });
334 } else if meta.path.is_ident("component") {
335 if out.component.is_some() {
336 return Err(meta.error("duplicate `component`"));
337 }
338 out.component_span = Some(meta.path.span());
339 let value = meta.value()?;
340 if value.peek(syn::LitStr) {
341 let lit = value.parse::<syn::LitStr>()?;
342 return Err(syn::Error::new(
343 lit.span(),
344 "put the data element identifier in `element`: \
345 `#[edifact(element = \"3055\")]` resolves both the element and the \
346 component position from the directory",
347 ));
348 }
349 let lit = value.parse::<syn::LitInt>()?;
350 let idx: u32 = lit.base10_parse()?;
351 check_index_bound(&lit, idx, "component")?;
352 out.component = Some(idx);
353 } else if meta.path.is_ident("composite") {
354 out.composite = true;
355 out.composite_span = Some(meta.path.span());
356 } else if meta.path.is_ident("group") {
357 out.group = true;
358 out.group_span = Some(meta.path.span());
359 } else if meta.path.is_ident("qualifier") {
360 if out.qualifier.is_some() {
361 return Err(meta.error("duplicate `qualifier`"));
362 }
363 out.qualifier = Some(meta.value()?.parse::<syn::LitStr>()?.value());
364 out.qualifier_span = Some(meta.path.span());
365 } else if meta.path.is_ident("required") {
366 out.required = true;
367 out.required_span = Some(meta.path.span());
368 } else {
369 return Err(meta.error("unknown field-level `edifact` key; expected `element`, `component`, `composite`, `group`, `qualifier`, or `required`"));
370 }
371 Ok(())
372 })?;
373 }
374 Ok(out)
375}
376
377struct Slots {
381 element: TokenStream2,
383 component: TokenStream2,
385 has_component: bool,
391 names_component: TokenStream2,
402}
403
404fn resolve_slots(
412 struct_attrs: &StructAttrs,
413 field_data: &[(&syn::Ident, &Type, FieldAttrs)],
414) -> syn::Result<(TokenStream2, Vec<Slots>)> {
415 let mut consts: Vec<TokenStream2> = Vec::new();
416 let mut slots: Vec<Slots> = Vec::with_capacity(field_data.len());
417 let mut slot_entries: Vec<TokenStream2> = Vec::new();
418
419 if struct_attrs.qualifier.is_some() {
420 slot_entries.push(quote! { (0usize, 0usize) });
422 }
423
424 for (i, (ident, _, attrs)) in field_data.iter().enumerate() {
425 if attrs.group {
426 slots.push(Slots {
427 element: quote! { 0usize },
428 component: quote! { 0usize },
429 has_component: false,
430 names_component: quote! { false },
431 });
432 continue;
433 }
434 let component_index = attrs.component.unwrap_or(0);
435 let slot = match &attrs.element {
436 Some(Position::Code(code)) => {
437 let Some(layout) = &struct_attrs.layout else {
438 return Err(syn::Error::new(
439 attrs.element_span.unwrap_or_else(|| ident.span()),
440 format!(
441 "field `{ident}`: `element = \"{code}\"` addresses a UN/EDIFACT data \
442 element identifier, which needs a directory to resolve against; add \
443 #[edifact(layout = path::to::SEGMENT_DEFINITION)] to the struct"
444 ),
445 ));
446 };
447 let slot_ident = syn::Ident::new(
448 &format!("__EDIFACT_SLOT_{i}"),
449 attrs.element_span.unwrap_or_else(|| ident.span()),
450 );
451 let unknown_msg = format!(
452 "field `{ident}`: data element {code} is not defined exactly once in the \
453 segment layout — check the identifier against the directory"
454 );
455 consts.push(quote! {
456 const _: () = ::core::assert!(#layout.code_positions(#code) == 1, #unknown_msg);
457 });
458 if attrs.component.is_some() {
459 let conflict_msg = format!(
460 "field `{ident}`: `component = N` may only accompany an identifier that \
461 names a whole data element, but {code} names a component inside one"
462 );
463 consts.push(quote! {
464 const _: () =
465 ::core::assert!(#layout.component_slot(#code) == 0, #conflict_msg);
466 });
467 consts.push(quote! {
468 const #slot_ident: (usize, usize) =
472 if #layout.code_positions(#code) == 1 {
473 (#layout.element_slot(#code), #component_index as usize)
474 } else {
475 (0, 0)
476 };
477 });
478 } else {
479 consts.push(quote! {
480 const #slot_ident: (usize, usize) =
481 if #layout.code_positions(#code) == 1 {
482 (#layout.element_slot(#code), #layout.component_slot(#code))
483 } else {
484 (0, 0)
485 };
486 });
487 }
488 slot_entries.push(quote! { #slot_ident });
489 let names_component = if attrs.component.is_some() {
490 quote! { true }
493 } else {
494 quote! { #layout.code_is_component(#code) }
495 };
496 Slots {
497 element: quote! { #slot_ident.0 },
498 component: quote! { #slot_ident.1 },
499 has_component: true,
500 names_component,
501 }
502 }
503 Some(Position::Index(idx)) => {
504 let idx = *idx;
505 let explicit = attrs.component.is_some();
506 slot_entries.push(quote! { (#idx as usize, #component_index as usize) });
507 Slots {
508 element: quote! { #idx as usize },
509 component: quote! { #component_index as usize },
510 has_component: explicit,
511 names_component: quote! { #explicit },
512 }
513 }
514 None => {
515 let idx = i as u32;
517 let explicit = attrs.component.is_some();
518 slot_entries.push(quote! { (#idx as usize, #component_index as usize) });
519 Slots {
520 element: quote! { #idx as usize },
521 component: quote! { #component_index as usize },
522 has_component: explicit,
523 names_component: quote! { #explicit },
524 }
525 }
526 };
527 slots.push(slot);
528 }
529
530 if struct_attrs.layout.is_some() && !slot_entries.is_empty() {
535 let count = slot_entries.len();
536 consts.push(quote! {
537 const __EDIFACT_SLOTS: [(usize, usize); #count] = [#(#slot_entries),*];
538 const _: () = {
539 let mut i = 0;
540 while i < __EDIFACT_SLOTS.len() {
541 let mut j = i + 1;
542 while j < __EDIFACT_SLOTS.len() {
543 ::core::assert!(
544 !(__EDIFACT_SLOTS[i].0 == __EDIFACT_SLOTS[j].0
545 && __EDIFACT_SLOTS[i].1 == __EDIFACT_SLOTS[j].1),
546 "two fields resolve to the same element/component slot; \
547 give each field a distinct data element identifier or index"
548 );
549 j += 1;
550 }
551 i += 1;
552 }
553 };
554 });
555 }
556
557 Ok((quote! { #(#consts)* }, slots))
558}
559
560fn is_option_type(ty: &Type) -> bool {
563 matches!(ty, Type::Path(p) if p.path.segments.last().is_some_and(|s| s.ident == "Option"))
564}
565
566fn is_vec_type(ty: &Type) -> bool {
567 matches!(ty, Type::Path(p) if p.path.segments.last().is_some_and(|s| s.ident == "Vec"))
568}
569
570fn is_string_type(ty: &Type) -> bool {
588 let Type::Path(p) = ty else { return false };
589 if p.path.is_ident("String") {
591 return true;
592 }
593 let segs = &p.path.segments;
595 segs.len() == 3
596 && (segs[0].ident == "std" || segs[0].ident == "alloc")
597 && segs[1].ident == "string"
598 && segs[2].ident == "String"
599}
600
601fn is_str_ref_type(ty: &Type) -> bool {
603 let Type::Reference(r) = ty else { return false };
604 matches!(r.elem.as_ref(), Type::Path(p) if p.path.is_ident("str"))
605}
606
607fn is_str_like(ty: &Type) -> bool {
610 is_string_type(ty) || is_str_ref_type(ty)
611}
612
613fn option_inner_type(ty: &Type) -> Option<&Type> {
614 let Type::Path(path) = ty else { return None };
615 let seg = path.path.segments.last()?;
616 if seg.ident != "Option" {
617 return None;
618 }
619 let syn::PathArguments::AngleBracketed(args) = &seg.arguments else {
620 return None;
621 };
622 let syn::GenericArgument::Type(inner) = args.args.first()? else {
623 return None;
624 };
625 Some(inner)
626}
627
628fn vec_inner_type(ty: &Type) -> Option<&Type> {
629 let Type::Path(path) = ty else { return None };
630 let seg = path.path.segments.last()?;
631 if seg.ident != "Vec" {
632 return None;
633 }
634 let syn::PathArguments::AngleBracketed(args) = &seg.arguments else {
635 return None;
636 };
637 let syn::GenericArgument::Type(inner) = args.args.first()? else {
638 return None;
639 };
640 Some(inner)
641}
642
643fn check_duplicate_slots(
655 field_data: &[(&syn::Ident, &Type, FieldAttrs)],
656 is_segment_struct: bool,
657) -> syn::Result<()> {
658 if !is_segment_struct {
659 return Ok(());
660 }
661 let mut seen: Vec<((u32, u32), &syn::Ident)> = Vec::with_capacity(field_data.len());
662 for (i, (ident, _, attrs)) in field_data.iter().enumerate() {
663 if attrs.group {
665 continue;
666 }
667 let element = match &attrs.element {
668 Some(Position::Index(idx)) => *idx,
669 Some(Position::Code(_)) => continue,
670 None => i as u32,
671 };
672 let slot = (element, attrs.component.unwrap_or(0));
673 if let Some((_, first)) = seen.iter().find(|(s, _)| *s == slot) {
674 return Err(syn::Error::new(
675 ident.span(),
676 format!(
677 "field `{ident}` maps to element {} component {}, which is already \
678 claimed by field `{first}`; give each field a distinct \
679 `#[edifact(element = ..., component = ...)]` slot",
680 slot.0, slot.1
681 ),
682 ));
683 }
684 seen.push((slot, ident));
685 }
686 Ok(())
687}
688
689fn get_named_fields(input: &DeriveInput) -> syn::Result<&syn::FieldsNamed> {
690 if !input.generics.params.is_empty() {
691 return Err(syn::Error::new(
692 input.generics.params.span(),
693 "EdifactSerialize/EdifactDeserialize do not support generic structs",
694 ));
695 }
696 match &input.data {
697 Data::Struct(s) => match &s.fields {
698 Fields::Named(f) => Ok(f),
699 _ => Err(syn::Error::new(
700 input.span(),
701 "EdifactSerialize/EdifactDeserialize only support structs with named fields",
702 )),
703 },
704 _ => Err(syn::Error::new(
705 input.span(),
706 "EdifactSerialize/EdifactDeserialize only support structs",
707 )),
708 }
709}
710
711fn validate_field_attrs(
712 ident: &syn::Ident,
713 ty: &Type,
714 attrs: &FieldAttrs,
715 is_segment_struct: bool,
716) -> syn::Result<()> {
717 if attrs.group && !is_vec_type(ty) {
718 return Err(syn::Error::new(
719 attrs.group_span.unwrap_or_else(|| ident.span()),
720 format!("field `{ident}`: #[edifact(group)] requires Vec<T>"),
721 ));
722 }
723 if attrs.group && (attrs.element.is_some() || attrs.component.is_some()) {
724 return Err(syn::Error::new(
725 attrs.group_span.unwrap_or_else(|| ident.span()),
726 format!(
727 "field `{ident}`: #[edifact(group)] cannot be combined with element/component positioning"
728 ),
729 ));
730 }
731 if attrs.composite && attrs.component.is_some() {
732 return Err(syn::Error::new(
733 attrs.component_span.unwrap_or_else(|| ident.span()),
734 format!(
735 "field `{ident}`: #[edifact(component = ...)] cannot be combined with #[edifact(composite)]"
736 ),
737 ));
738 }
739 if attrs.composite && attrs.group {
740 return Err(syn::Error::new(
741 attrs.composite_span.unwrap_or_else(|| ident.span()),
742 format!(
743 "field `{ident}`: #[edifact(composite)] cannot be combined with #[edifact(group)]"
744 ),
745 ));
746 }
747 if is_segment_struct && attrs.group {
748 return Err(syn::Error::new(
749 attrs.group_span.unwrap_or_else(|| ident.span()),
750 format!("field `{ident}`: #[edifact(group)] is only valid on message structs"),
751 ));
752 }
753 if !is_segment_struct && (attrs.element.is_some() || attrs.component.is_some()) {
754 return Err(syn::Error::new(
755 attrs
756 .element_span
757 .or(attrs.component_span)
758 .unwrap_or_else(|| ident.span()),
759 format!(
760 "field `{ident}`: element/component positioning is only valid on segment structs"
761 ),
762 ));
763 }
764 if !is_segment_struct && attrs.composite {
765 return Err(syn::Error::new(
766 attrs.composite_span.unwrap_or_else(|| ident.span()),
767 format!("field `{ident}`: #[edifact(composite)] is only valid on segment structs"),
768 ));
769 }
770 if is_segment_struct && attrs.qualifier.is_some() {
771 return Err(syn::Error::new(
772 attrs.qualifier_span.unwrap_or_else(|| ident.span()),
773 format!(
774 "field `{ident}`: #[edifact(qualifier = ...)] is only valid on message struct fields"
775 ),
776 ));
777 }
778 if attrs.qualifier.is_some() && attrs.group && !is_vec_type(ty) {
779 return Err(syn::Error::new(
780 attrs.qualifier_span.unwrap_or_else(|| ident.span()),
781 format!("field `{ident}`: qualifier-constrained groups must be Vec<T>"),
782 ));
783 }
784 if attrs.required && !is_option_type(ty) {
785 return Err(syn::Error::new(
786 attrs.required_span.unwrap_or_else(|| ident.span()),
787 format!(
788 "field `{ident}`: #[edifact(required)] only applies to Option<T> fields; \
789 non-Option fields are always required"
790 ),
791 ));
792 }
793 if attrs.required && attrs.composite {
794 return Err(syn::Error::new(
795 attrs.required_span.unwrap_or_else(|| ident.span()),
796 format!(
797 "field `{ident}`: #[edifact(required)] cannot be combined with \
798 #[edifact(composite)]; use a non-optional field type to require the \
799 composite element"
800 ),
801 ));
802 }
803 if attrs.required && !is_segment_struct {
804 return Err(syn::Error::new(
805 attrs.required_span.unwrap_or_else(|| ident.span()),
806 format!(
807 "field `{ident}`: #[edifact(required)] is only valid on segment struct \
808 element fields; to require a segment in a message struct, use a \
809 non-optional field type"
810 ),
811 ));
812 }
813 Ok(())
814}
815
816fn static_element_index(attrs: &FieldAttrs, decl_index: usize) -> u32 {
823 match &attrs.element {
824 Some(Position::Index(idx)) => *idx,
825 _ => decl_index as u32,
826 }
827}
828
829fn impl_serialize_sparse(
840 name: &syn::Ident,
841 seg_tag: &str,
842 struct_attrs: &StructAttrs,
843 field_data: &[(&syn::Ident, &Type, FieldAttrs)],
844 slots: &[Slots],
845 slot_prelude: &TokenStream2,
846) -> TokenStream2 {
847 let mut stmts: Vec<TokenStream2> = Vec::new();
848
849 if let Some(qual) = &struct_attrs.qualifier {
850 stmts.push(quote! {
851 __parts.push((0usize, 0usize, ::std::borrow::Cow::Borrowed(#qual)));
852 });
853 }
854
855 for ((ident, ty, attrs), slot) in field_data.iter().zip(slots) {
856 let (element, component) = (&slot.element, &slot.component);
857 if attrs.composite {
858 let serialize_composite = if is_option_type(ty) {
861 quote! {
862 if let ::core::option::Option::Some(__v) = &self.#ident {
863 ::edifact_rs::EdifactCompositeSerialize::edifact_serialize_composite(
864 __v, &mut __sub,
865 )?;
866 }
867 }
868 } else {
869 quote! {
870 ::edifact_rs::EdifactCompositeSerialize::edifact_serialize_composite(
871 &self.#ident, &mut __sub,
872 )?;
873 }
874 };
875 stmts.push(quote! {
876 {
877 let mut __sub = ::edifact_rs::VecEmitter::default();
878 #serialize_composite
879 let mut __comp = 0usize;
880 let mut __any = false;
881 for __event in __sub.events {
882 match __event {
883 ::edifact_rs::OwnedEdifactEvent::Element { value } => {
884 __comp = 0;
885 __any = true;
886 __parts.push((#element, 0usize, ::std::borrow::Cow::Owned(value)));
887 }
888 ::edifact_rs::OwnedEdifactEvent::ComponentElement { value } => {
889 __comp += 1;
890 __any = true;
891 __parts.push((#element, __comp, ::std::borrow::Cow::Owned(value)));
892 }
893 _ => {}
894 }
895 }
896 if !__any {
897 __parts.push((#element, 0usize, ::std::borrow::Cow::Borrowed("")));
898 }
899 }
900 });
901 continue;
902 }
903
904 let value_expr = if is_option_type(ty) {
905 let inner_is_str = option_inner_type(ty).is_some_and(is_str_like);
906 if inner_is_str {
907 quote! {
908 match &self.#ident {
909 ::core::option::Option::Some(__v) => ::std::borrow::Cow::Borrowed(__v.as_str()),
910 ::core::option::Option::None => ::std::borrow::Cow::Borrowed(""),
911 }
912 }
913 } else {
914 quote! {
915 match &self.#ident {
916 ::core::option::Option::Some(__v) => {
917 ::std::borrow::Cow::Owned(::std::string::ToString::to_string(__v))
918 }
919 ::core::option::Option::None => ::std::borrow::Cow::Borrowed(""),
920 }
921 }
922 }
923 } else if is_string_type(ty) {
924 quote! { ::std::borrow::Cow::Borrowed(self.#ident.as_str()) }
925 } else if is_str_ref_type(ty) {
926 quote! { ::std::borrow::Cow::Borrowed(self.#ident) }
927 } else {
928 quote! { ::std::borrow::Cow::Owned(::std::string::ToString::to_string(&self.#ident)) }
929 };
930
931 stmts.push(quote! {
932 __parts.push((#element, #component, #value_expr));
933 });
934 }
935
936 let capacity = field_data.len() + usize::from(struct_attrs.qualifier.is_some());
937
938 quote! {
939 impl ::edifact_rs::EdifactSerialize for #name {
940 fn edifact_serialize<__E: ::edifact_rs::EventEmitter>(
941 &self,
942 emitter: &mut __E,
943 ) -> ::core::result::Result<(), ::edifact_rs::EdifactError> {
944 #slot_prelude
945 let mut __parts: ::std::vec::Vec<(
946 usize,
947 usize,
948 ::std::borrow::Cow<'_, str>,
949 )> = ::std::vec::Vec::with_capacity(#capacity);
950 #(#stmts)*
951 ::edifact_rs::emit_sparse_segment(emitter, #seg_tag, &mut __parts)
952 }
953 }
954 }
955}
956
957fn impl_serialize(input: &DeriveInput) -> syn::Result<TokenStream2> {
958 let name = &input.ident;
959 let struct_attrs = parse_struct_attrs(input)?;
960 let fields = get_named_fields(input)?;
961 let is_segment_struct = struct_attrs.segment.is_some();
962
963 let field_data: Vec<(&syn::Ident, &Type, FieldAttrs)> = fields
965 .named
966 .iter()
967 .map(|f| {
968 let attrs = parse_field_attrs(f)?;
969 let ident = f
970 .ident
971 .as_ref()
972 .ok_or_else(|| syn::Error::new_spanned(f, "only named fields are supported"))?;
973 validate_field_attrs(ident, &f.ty, &attrs, is_segment_struct)?;
974 Ok((ident, &f.ty, attrs))
975 })
976 .collect::<syn::Result<_>>()?;
977 check_duplicate_slots(&field_data, is_segment_struct)?;
978 let (slot_prelude, slots) = resolve_slots(&struct_attrs, &field_data)?;
979 let uses_code_slots = field_data
980 .iter()
981 .any(|(_, _, attrs)| matches!(attrs.element, Some(Position::Code(_))));
982
983 let body = if let Some(seg_tag) = &struct_attrs.segment {
984 if uses_code_slots {
985 return Ok(impl_serialize_sparse(
986 name,
987 seg_tag,
988 &struct_attrs,
989 &field_data,
990 &slots,
991 &slot_prelude,
992 ));
993 }
994 let (qualifier_emit, start_slot, elem0_comp_stmts) = if let Some(qual) =
999 &struct_attrs.qualifier
1000 {
1001 for (i, (ident, _, attrs)) in field_data.iter().enumerate() {
1003 let elem = static_element_index(attrs, i);
1004 let comp = attrs.component.unwrap_or(0);
1005 if elem == 0 && comp == 0 {
1006 return Err(syn::Error::new(
1007 attrs
1008 .element_span
1009 .or(attrs.component_span)
1010 .unwrap_or_else(|| ident.span()),
1011 format!(
1012 "field `{}`: cannot use #[edifact(qualifier = ...)] with a field at element = 0 without component >= 1; the qualifier occupies component 0",
1013 ident
1014 ),
1015 ));
1016 }
1017 }
1018 let mut comp_fields: Vec<(u32, usize)> = field_data
1020 .iter()
1021 .enumerate()
1022 .filter_map(|(i, (_, _, attrs))| {
1023 let elem = static_element_index(attrs, i);
1024 let comp = attrs.component.unwrap_or(0);
1025 if elem == 0 && comp > 0 {
1026 Some((comp, i))
1027 } else {
1028 None
1029 }
1030 })
1031 .collect();
1032 comp_fields.sort_by_key(|(c, _)| *c);
1033 let comp_stmts: Vec<TokenStream2> = comp_fields
1034 .iter()
1035 .map(|(_, fi)| {
1036 let (ident, ty, _) = &field_data[*fi];
1037 emit_component_element(ident, ty)
1038 })
1039 .collect();
1040 let q = quote! {
1041 emitter.emit(::edifact_rs::EdifactEvent::Element { value: #qual })?;
1042 };
1043 (q, 1u32, quote! { #(#comp_stmts)* })
1044 } else {
1045 (quote! {}, 0u32, quote! {})
1046 };
1047
1048 let regular_field_data: Vec<(u32, usize)> = field_data
1050 .iter()
1051 .enumerate()
1052 .filter_map(|(i, (_, _, attrs))| {
1053 let elem = static_element_index(attrs, i);
1054 if elem < start_slot {
1055 None
1056 } else {
1057 Some((elem, i))
1058 }
1059 })
1060 .collect();
1061 let reg_max_idx = regular_field_data
1062 .iter()
1063 .map(|(e, _)| *e)
1064 .max()
1065 .unwrap_or(start_slot.saturating_sub(1));
1066 let reg_field_map: std::collections::HashMap<u32, usize> =
1067 regular_field_data.iter().copied().collect();
1068
1069 let mut elem_stmts: Vec<TokenStream2> = Vec::new();
1070 for slot in start_slot..=reg_max_idx {
1071 if let Some(&fi) = reg_field_map.get(&slot) {
1072 let (ident, ty, attrs) = &field_data[fi];
1073 if attrs.composite {
1074 elem_stmts.push(emit_composite_field(ident, ty));
1075 } else {
1076 elem_stmts.push(emit_element(ident, ty));
1077 }
1078 } else {
1079 elem_stmts.push(quote! {
1081 emitter.emit(::edifact_rs::EdifactEvent::Element { value: "" })?;
1082 });
1083 }
1084 }
1085
1086 quote! {
1087 emitter.emit(::edifact_rs::EdifactEvent::StartSegment { tag: #seg_tag })?;
1088 #qualifier_emit
1089 #elem0_comp_stmts
1090 #(#elem_stmts)*
1091 emitter.emit(::edifact_rs::EdifactEvent::EndSegment)?;
1092 }
1093 } else {
1094 let stmts: Vec<TokenStream2> = field_data
1096 .iter()
1097 .map(|(ident, ty, attrs)| {
1098 if attrs.group || is_vec_type(ty) {
1099 quote! {
1100 for __item in &self.#ident {
1101 ::edifact_rs::EdifactSerialize::edifact_serialize(__item, emitter)?;
1102 }
1103 }
1104 } else {
1105 quote! {
1106 ::edifact_rs::EdifactSerialize::edifact_serialize(&self.#ident, emitter)?;
1107 }
1108 }
1109 })
1110 .collect();
1111 quote! { #(#stmts)* }
1112 };
1113
1114 Ok(quote! {
1115 impl ::edifact_rs::EdifactSerialize for #name {
1116 fn edifact_serialize<__E: ::edifact_rs::EventEmitter>(
1117 &self,
1118 emitter: &mut __E,
1119 ) -> ::core::result::Result<(), ::edifact_rs::EdifactError> {
1120 #body
1121 ::core::result::Result::Ok(())
1122 }
1123 }
1124 })
1125}
1126
1127fn emit_element(ident: &syn::Ident, ty: &Type) -> TokenStream2 {
1132 if is_option_type(ty) {
1133 let inner_is_str = option_inner_type(ty).is_some_and(is_str_like);
1134 if inner_is_str {
1135 quote! {
1136 match &self.#ident {
1137 ::core::option::Option::Some(__v) => {
1138 emitter.emit(::edifact_rs::EdifactEvent::Element { value: __v.as_str() })?;
1139 }
1140 ::core::option::Option::None => {
1141 emitter.emit(::edifact_rs::EdifactEvent::Element { value: "" })?;
1142 }
1143 }
1144 }
1145 } else {
1146 quote! {
1147 match &self.#ident {
1148 ::core::option::Option::Some(__v) => {
1149 let __s = ::std::string::ToString::to_string(__v);
1150 emitter.emit(::edifact_rs::EdifactEvent::Element { value: &__s })?;
1151 }
1152 ::core::option::Option::None => {
1153 emitter.emit(::edifact_rs::EdifactEvent::Element { value: "" })?;
1154 }
1155 }
1156 }
1157 }
1158 } else if is_string_type(ty) {
1159 quote! {
1160 emitter.emit(::edifact_rs::EdifactEvent::Element { value: self.#ident.as_str() })?;
1161 }
1162 } else if is_str_ref_type(ty) {
1163 quote! {
1164 emitter.emit(::edifact_rs::EdifactEvent::Element { value: self.#ident })?;
1165 }
1166 } else {
1167 quote! {
1168 {
1169 let __s = ::std::string::ToString::to_string(&self.#ident);
1170 emitter.emit(::edifact_rs::EdifactEvent::Element { value: &__s })?;
1171 }
1172 }
1173 }
1174}
1175
1176fn emit_component_element(ident: &syn::Ident, ty: &Type) -> TokenStream2 {
1180 if is_option_type(ty) {
1181 let inner_is_str = option_inner_type(ty).is_some_and(is_str_like);
1182 if inner_is_str {
1183 quote! {
1184 match &self.#ident {
1185 ::core::option::Option::Some(__v) => {
1186 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: __v.as_str() })?;
1187 }
1188 ::core::option::Option::None => {
1189 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: "" })?;
1190 }
1191 }
1192 }
1193 } else {
1194 quote! {
1195 match &self.#ident {
1196 ::core::option::Option::Some(__v) => {
1197 let __s = ::std::string::ToString::to_string(__v);
1198 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: &__s })?;
1199 }
1200 ::core::option::Option::None => {
1201 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: "" })?;
1202 }
1203 }
1204 }
1205 }
1206 } else if is_string_type(ty) {
1207 quote! {
1208 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: self.#ident.as_str() })?;
1209 }
1210 } else if is_str_ref_type(ty) {
1211 quote! {
1212 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: self.#ident })?;
1213 }
1214 } else {
1215 quote! {
1216 {
1217 let __s = ::std::string::ToString::to_string(&self.#ident);
1218 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: &__s })?;
1219 }
1220 }
1221 }
1222}
1223
1224fn emit_composite_field(ident: &syn::Ident, ty: &Type) -> TokenStream2 {
1226 if is_option_type(ty) {
1227 quote! {
1228 match &self.#ident {
1229 ::core::option::Option::Some(__v) => {
1230 ::edifact_rs::EdifactCompositeSerialize::edifact_serialize_composite(__v, emitter)?;
1231 }
1232 ::core::option::Option::None => {
1233 emitter.emit(::edifact_rs::EdifactEvent::Element { value: "" })?;
1234 }
1235 }
1236 }
1237 } else {
1238 quote! {
1239 ::edifact_rs::EdifactCompositeSerialize::edifact_serialize_composite(&self.#ident, emitter)?;
1240 }
1241 }
1242}
1243
1244fn impl_deserialize(input: &DeriveInput) -> syn::Result<TokenStream2> {
1247 let name = &input.ident;
1248 let struct_attrs = parse_struct_attrs(input)?;
1249 let fields = get_named_fields(input)?;
1250 let is_segment_struct = struct_attrs.segment.is_some();
1251
1252 let field_data: Vec<(&syn::Ident, &Type, FieldAttrs)> = fields
1253 .named
1254 .iter()
1255 .map(|f| {
1256 let attrs = parse_field_attrs(f)?;
1257 let ident = f
1258 .ident
1259 .as_ref()
1260 .ok_or_else(|| syn::Error::new_spanned(f, "only named fields are supported"))?;
1261 validate_field_attrs(ident, &f.ty, &attrs, is_segment_struct)?;
1262 Ok((ident, &f.ty, attrs))
1263 })
1264 .collect::<syn::Result<_>>()?;
1265 check_duplicate_slots(&field_data, is_segment_struct)?;
1266 let (slot_prelude, slots) = resolve_slots(&struct_attrs, &field_data)?;
1267
1268 let field_names: Vec<&syn::Ident> = field_data.iter().map(|(id, _, _)| *id).collect();
1269
1270 let (body, owned_body, segment_tag_impl) = if let Some(seg_tag) = &struct_attrs.segment {
1271 let qualifier_guard = if let Some(qual) = &struct_attrs.qualifier {
1273 quote! {
1274 if __seg.element_str(0).unwrap_or("") != #qual {
1275 return ::core::result::Result::Err(
1276 ::edifact_rs::EdifactError::MissingRequiredElement {
1277 tag: #seg_tag.to_owned(),
1278 element_index: 0,
1279 }
1280 );
1281 }
1282 }
1283 } else if let Some(idx) = struct_attrs.qualifier_from {
1284 quote! {
1285 match __seg.element_str(#idx as usize) {
1289 ::core::option::Option::None => return ::core::result::Result::Err(
1290 ::edifact_rs::EdifactError::MissingRequiredElement {
1291 tag: #seg_tag.to_owned(),
1292 element_index: #idx as usize,
1293 }
1294 ),
1295 ::core::option::Option::Some("") => return ::core::result::Result::Err(
1296 ::edifact_rs::EdifactError::InvalidFieldValue {
1297 tag: #seg_tag.to_owned(),
1298 element_index: #idx as usize,
1299 value: ::std::string::String::new(),
1300 }
1301 ),
1302 ::core::option::Option::Some(__qual_val) => { let _ = __qual_val; }
1303 }
1304 }
1305 } else {
1306 quote! {}
1307 };
1308
1309 let find_seg = if let Some(qual) = &struct_attrs.qualifier {
1310 quote! {
1311 ::edifact_rs::find_qualified_segment(segments, #seg_tag, #qual)
1312 }
1313 } else {
1314 quote! {
1315 ::edifact_rs::find_segment(segments, #seg_tag)
1316 }
1317 };
1318
1319 let field_inits: Vec<TokenStream2> = field_data
1320 .iter()
1321 .zip(slots.iter())
1322 .map(|((ident, ty, attrs), slot)| -> syn::Result<TokenStream2> {
1323 let idx = &slot.element;
1324 if attrs.composite {
1325 if is_option_type(ty) {
1326 let inner_ty = option_inner_type(ty)
1327 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1328 return Ok(quote! {
1329 let #ident = match ::edifact_rs::composite_element(__seg, #idx) {
1330 ::core::option::Option::Some(__composite) => {
1331 ::core::option::Option::Some(
1332 <#inner_ty as ::edifact_rs::EdifactCompositeDeserialize>::edifact_deserialize_composite(__composite)?
1333 )
1334 }
1335 ::core::option::Option::None => ::core::option::Option::None,
1336 };
1337 });
1338 }
1339 return Ok(quote! {
1340 let #ident = <#ty as ::edifact_rs::EdifactCompositeDeserialize>::edifact_deserialize_composite(
1341 ::edifact_rs::composite_element(__seg, #idx).ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
1342 tag: #seg_tag.to_owned(),
1343 element_index: #idx,
1344 })?
1345 )?;
1346 });
1347 }
1348 let comp = &slot.component;
1349 let value_expr = if slot.has_component {
1350 quote! {
1351 __seg.get_element(#idx).and_then(|__e| __e.get_component(#comp))
1352 }
1353 } else {
1354 quote! { __seg.element_str(#idx) }
1355 };
1356 let names_component = &slot.names_component;
1360 let missing_required_err = quote! {
1361 if #names_component {
1362 ::edifact_rs::EdifactError::MissingRequiredComponent {
1363 tag: #seg_tag.to_owned(),
1364 element_index: #idx,
1365 component_index: #comp,
1366 }
1367 } else {
1368 ::edifact_rs::EdifactError::MissingRequiredElement {
1369 tag: #seg_tag.to_owned(),
1370 element_index: #idx,
1371 }
1372 }
1373 };
1374 Ok(if is_option_type(ty) {
1375 let inner_ty = option_inner_type(ty);
1376 let inner_is_str = inner_ty.is_some_and(is_str_like);
1377 if attrs.required {
1378 if inner_is_str {
1382 quote! {
1383 let #ident = ::core::option::Option::Some(
1384 #value_expr
1385 .filter(|__s| !__s.is_empty())
1386 .ok_or_else(|| #missing_required_err)?
1387 .to_owned()
1388 );
1389 }
1390 } else {
1391 let inner_ty = inner_ty
1392 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1393 quote! {
1394 let #ident = ::core::option::Option::Some(
1395 #value_expr
1396 .filter(|__s| !__s.is_empty())
1397 .ok_or_else(|| #missing_required_err)?
1398 .parse::<#inner_ty>()
1399 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })?
1400 );
1401 }
1402 }
1403 } else if inner_is_str {
1404 quote! {
1405 let #ident = #value_expr
1406 .filter(|__s| !__s.is_empty())
1407 .map(::std::string::String::from);
1408 }
1409 } else {
1410 let inner_ty = inner_ty
1411 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1412 quote! {
1413 let #ident = #value_expr
1414 .filter(|__s| !__s.is_empty())
1415 .map(|__s| __s.parse::<#inner_ty>()
1416 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })
1417 )
1418 .transpose()?;
1419 }
1420 }
1421 } else if is_str_like(ty) {
1422 quote! {
1423 let #ident = #value_expr
1424 .filter(|__s| !__s.is_empty())
1425 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
1426 tag: #seg_tag.to_owned(),
1427 element_index: #idx,
1428 })?
1429 .to_owned();
1430 }
1431 } else {
1432 quote! {
1433 let #ident = #value_expr
1434 .filter(|__s| !__s.is_empty())
1435 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
1436 tag: #seg_tag.to_owned(),
1437 element_index: #idx,
1438 })?
1439 .parse::<#ty>()
1440 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })?;
1441 }
1442 })
1443 })
1444 .collect::<syn::Result<_>>()?;
1445
1446 let body = quote! {
1447 #slot_prelude
1448 let __seg = #find_seg
1449 .ok_or_else(|| ::edifact_rs::EdifactError::MissingSegment {
1450 tag: #seg_tag.to_owned(),
1451 expected_position: "message body".to_owned(),
1452 })?;
1453 #qualifier_guard
1454 #(#field_inits)*
1455 ::core::result::Result::Ok(Self { #(#field_names),* })
1456 };
1457
1458 let qualifier_match = if let Some(qual) = &struct_attrs.qualifier {
1465 quote! {
1466 const QUALIFIER_PATTERN: ::core::option::Option<&'static str> =
1467 ::core::option::Option::Some(#qual);
1468 }
1469 } else if let Some(idx) = struct_attrs.qualifier_from {
1470 quote! {
1474 fn matches_segment(seg: &::edifact_rs::Segment<'_>) -> bool {
1475 seg.tag == Self::SEGMENT_TAG
1476 && !seg.element_str(#idx as usize).unwrap_or("").is_empty()
1477 }
1478
1479 fn matches_owned_segment(seg: &::edifact_rs::OwnedSegment) -> bool {
1480 seg.tag == Self::SEGMENT_TAG
1481 && !seg
1482 .elements
1483 .get(#idx as usize)
1484 .and_then(|e| e.components.first())
1485 .map(|(c, _)| c.as_str())
1486 .unwrap_or("")
1487 .is_empty()
1488 }
1489 }
1490 } else {
1491 quote! {}
1492 };
1493
1494 let seg_tag_impl = quote! {
1495 impl ::edifact_rs::EdifactSegmentTag for #name {
1496 const SEGMENT_TAG: &'static str = #seg_tag;
1497 #qualifier_match
1498 }
1499 };
1500
1501 let find_seg_owned = if let Some(qual) = &struct_attrs.qualifier {
1504 quote! {
1505 ::edifact_rs::find_qualified_segment_owned(segments, #seg_tag, #qual)
1506 }
1507 } else {
1508 quote! {
1509 ::edifact_rs::find_segment_owned(segments, #seg_tag)
1510 }
1511 };
1512
1513 let field_inits_owned: Vec<TokenStream2> = field_data
1514 .iter()
1515 .zip(slots.iter())
1516 .map(|((ident, ty, attrs), slot)| -> syn::Result<TokenStream2> {
1517 let idx = &slot.element;
1518 if attrs.composite {
1519 if is_option_type(ty) {
1520 let inner_ty = option_inner_type(ty)
1521 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1522 return Ok(quote! {
1523 let #ident = match __seg.elements.get(#idx) {
1524 ::core::option::Option::Some(__e) => {
1525 let __cows = __e.components.iter()
1526 .map(|(s, _)| ::std::borrow::Cow::Borrowed(s.as_str()))
1527 .collect::<::std::vec::Vec<::std::borrow::Cow<'_, str>>>();
1528 ::core::option::Option::Some(
1529 <#inner_ty as ::edifact_rs::EdifactCompositeDeserialize>::edifact_deserialize_composite(
1530 ::edifact_rs::CompositeElement::from_slice(&__cows)
1531 )?
1532 )
1533 }
1534 ::core::option::Option::None => ::core::option::Option::None,
1535 };
1536 });
1537 }
1538 return Ok(quote! {
1539 let #ident = {
1540 let __cows = __seg.elements.get(#idx)
1541 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
1542 tag: #seg_tag.to_owned(),
1543 element_index: #idx,
1544 })?
1545 .components.iter()
1546 .map(|(s, _)| ::std::borrow::Cow::Borrowed(s.as_str()))
1547 .collect::<::std::vec::Vec<::std::borrow::Cow<'_, str>>>();
1548 <#ty as ::edifact_rs::EdifactCompositeDeserialize>::edifact_deserialize_composite(
1549 ::edifact_rs::CompositeElement::from_slice(&__cows)
1550 )?
1551 };
1552 });
1553 }
1554 let comp = &slot.component;
1555 let value_expr_owned = if slot.has_component {
1556 quote! { __seg.component_str(#idx, #comp) }
1557 } else {
1558 quote! { __seg.element_str(#idx) }
1559 };
1560 let names_component = &slot.names_component;
1563 let missing_required_err_owned = quote! {
1564 if #names_component {
1565 ::edifact_rs::EdifactError::MissingRequiredComponent {
1566 tag: #seg_tag.to_owned(),
1567 element_index: #idx,
1568 component_index: #comp,
1569 }
1570 } else {
1571 ::edifact_rs::EdifactError::MissingRequiredElement {
1572 tag: #seg_tag.to_owned(),
1573 element_index: #idx,
1574 }
1575 }
1576 };
1577 Ok(if is_option_type(ty) {
1578 if let Some(inner_ty) = option_inner_type(ty) {
1579 if attrs.required {
1580 if is_str_like(inner_ty) {
1584 quote! {
1585 let #ident = ::core::option::Option::Some(
1586 #value_expr_owned
1587 .filter(|__s| !__s.is_empty())
1588 .ok_or_else(|| #missing_required_err_owned)?
1589 .to_owned()
1590 );
1591 }
1592 } else {
1593 quote! {
1594 let #ident = ::core::option::Option::Some(
1595 #value_expr_owned
1596 .filter(|__s| !__s.is_empty())
1597 .ok_or_else(|| #missing_required_err_owned)?
1598 .parse::<#inner_ty>()
1599 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })?
1600 );
1601 }
1602 }
1603 } else if is_str_like(inner_ty) {
1604 quote! {
1605 let #ident = #value_expr_owned
1606 .filter(|__s| !__s.is_empty())
1607 .map(::std::string::String::from);
1608 }
1609 } else {
1610 quote! {
1611 let #ident = #value_expr_owned
1612 .filter(|__s| !__s.is_empty())
1613 .map(|__s| __s.parse::<#inner_ty>()
1614 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })
1615 )
1616 .transpose()?;
1617 }
1618 }
1619 } else {
1620 quote! {
1622 let #ident = #value_expr_owned
1623 .filter(|__s| !__s.is_empty())
1624 .map(::std::string::String::from);
1625 }
1626 }
1627 } else if is_str_like(ty) {
1628 quote! {
1629 let #ident = #value_expr_owned
1630 .filter(|__s| !__s.is_empty())
1631 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
1632 tag: #seg_tag.to_owned(),
1633 element_index: #idx,
1634 })?
1635 .to_owned();
1636 }
1637 } else {
1638 quote! {
1639 let #ident = #value_expr_owned
1640 .filter(|__s| !__s.is_empty())
1641 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
1642 tag: #seg_tag.to_owned(),
1643 element_index: #idx,
1644 })?
1645 .parse::<#ty>()
1646 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })?;
1647 }
1648 })
1649 })
1650 .collect::<syn::Result<_>>()?;
1651
1652 let owned_body = quote! {
1653 #slot_prelude
1654 let __seg = #find_seg_owned
1655 .ok_or_else(|| ::edifact_rs::EdifactError::MissingSegment {
1656 tag: #seg_tag.to_owned(),
1657 expected_position: "message body".to_owned(),
1658 })?;
1659 #qualifier_guard
1660 #(#field_inits_owned)*
1661 ::core::result::Result::Ok(Self { #(#field_names),* })
1662 };
1663
1664 (body, owned_body, seg_tag_impl)
1665 } else {
1666 let field_inits: Vec<TokenStream2> = field_data
1668 .iter()
1669 .map(|(ident, ty, attrs)| -> syn::Result<TokenStream2> {
1670 Ok(if let Some(qual) = &attrs.qualifier {
1671 if attrs.group || is_vec_type(ty) {
1672 let inner_ty = vec_inner_type(ty)
1673 .ok_or_else(|| syn::Error::new(ident.span(), "expected Vec<T>"))?;
1674 quote! {
1675 let #ident = segments
1676 .iter()
1677 .filter(|__seg| {
1678 __seg.tag == <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG
1679 && __seg.element_str(0).unwrap_or("") == #qual
1680 })
1681 .map(|__seg| {
1682 ::edifact_rs::EdifactDeserialize::edifact_deserialize(
1683 ::core::slice::from_ref(__seg),
1684 )
1685 })
1686 .collect::<::core::result::Result<::std::vec::Vec<#inner_ty>, ::edifact_rs::EdifactError>>()?;
1687 }
1688 } else if is_option_type(ty) {
1689 let inner_ty = option_inner_type(ty)
1690 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1691 quote! {
1692 let #ident = match ::edifact_rs::find_qualified_segment(
1693 segments,
1694 <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG,
1695 #qual,
1696 ) {
1697 ::core::option::Option::Some(__seg) => {
1698 ::core::option::Option::Some(
1699 ::edifact_rs::EdifactDeserialize::edifact_deserialize(
1700 ::core::slice::from_ref(__seg),
1701 )?
1702 )
1703 }
1704 ::core::option::Option::None => ::core::option::Option::None,
1705 };
1706 }
1707 } else {
1708 quote! {
1709 let __seg = ::edifact_rs::find_qualified_segment(
1710 segments,
1711 <#ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG,
1712 #qual,
1713 )
1714 .ok_or_else(|| ::edifact_rs::EdifactError::MissingSegment {
1715 tag: <#ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG.to_owned(),
1716 expected_position: "message body".to_owned(),
1717 })?;
1718 let #ident = ::edifact_rs::EdifactDeserialize::edifact_deserialize(
1719 ::core::slice::from_ref(__seg),
1720 )?;
1721 }
1722 }
1723 } else if attrs.group || is_vec_type(ty) {
1724 let inner_ty = vec_inner_type(ty)
1725 .ok_or_else(|| syn::Error::new(ident.span(), "expected Vec<T>"))?;
1726 quote! {
1727 let #ident = ::edifact_rs::find_segments_typed::<#inner_ty>(segments)
1728 .map(|__seg| {
1729 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize(
1730 ::core::slice::from_ref(__seg),
1731 )
1732 })
1733 .collect::<::core::result::Result<::std::vec::Vec<#inner_ty>, _>>()?;
1734 }
1735 } else if is_option_type(ty) {
1736 let inner_ty = option_inner_type(ty)
1737 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1738 quote! {
1739 let #ident = if segments
1740 .iter()
1741 .any(|__seg| __seg.tag == <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG)
1742 {
1743 ::core::option::Option::Some(
1744 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize(segments)?
1745 )
1746 } else {
1747 ::core::option::Option::None
1748 };
1749 }
1750 } else {
1751 quote! {
1752 let #ident = ::edifact_rs::EdifactDeserialize::edifact_deserialize(segments)?;
1753 }
1754 })
1755 })
1756 .collect::<syn::Result<_>>()?;
1757
1758 let body = quote! {
1759 #(#field_inits)*
1760 ::core::result::Result::Ok(Self { #(#field_names),* })
1761 };
1762
1763 let field_inits_owned: Vec<TokenStream2> = field_data
1766 .iter()
1767 .map(|(ident, ty, attrs)| -> syn::Result<TokenStream2> {
1768 Ok(if let Some(qual) = &attrs.qualifier {
1769 if attrs.group || is_vec_type(ty) {
1770 let inner_ty = vec_inner_type(ty)
1771 .ok_or_else(|| syn::Error::new(ident.span(), "expected Vec<T>"))?;
1772 quote! {
1773 let #ident = segments
1774 .iter()
1775 .filter(|__seg| {
1776 __seg.tag == <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG
1777 && __seg.element_str(0).unwrap_or("") == #qual
1778 })
1779 .map(|__seg| {
1780 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(
1781 ::core::slice::from_ref(__seg),
1782 )
1783 })
1784 .collect::<::core::result::Result<::std::vec::Vec<#inner_ty>, ::edifact_rs::EdifactError>>()?;
1785 }
1786 } else if is_option_type(ty) {
1787 let inner_ty = option_inner_type(ty)
1788 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1789 quote! {
1790 let #ident = match ::edifact_rs::find_qualified_segment_owned(
1791 segments,
1792 <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG,
1793 #qual,
1794 ) {
1795 ::core::option::Option::Some(__seg) => {
1796 ::core::option::Option::Some(
1797 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(
1798 ::core::slice::from_ref(__seg),
1799 )?
1800 )
1801 }
1802 ::core::option::Option::None => ::core::option::Option::None,
1803 };
1804 }
1805 } else {
1806 quote! {
1807 let __seg = ::edifact_rs::find_qualified_segment_owned(
1808 segments,
1809 <#ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG,
1810 #qual,
1811 )
1812 .ok_or_else(|| ::edifact_rs::EdifactError::MissingSegment {
1813 tag: <#ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG.to_owned(),
1814 expected_position: "message body".to_owned(),
1815 })?;
1816 let #ident = <#ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(
1817 ::core::slice::from_ref(__seg),
1818 )?;
1819 }
1820 }
1821 } else if attrs.group || is_vec_type(ty) {
1822 let inner_ty = vec_inner_type(ty)
1823 .ok_or_else(|| syn::Error::new(ident.span(), "expected Vec<T>"))?;
1824 quote! {
1825 let #ident = segments
1826 .iter()
1827 .filter(|__seg| <#inner_ty as ::edifact_rs::EdifactSegmentTag>::matches_owned_segment(__seg))
1828 .map(|__seg| {
1829 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(
1830 ::core::slice::from_ref(__seg),
1831 )
1832 })
1833 .collect::<::core::result::Result<::std::vec::Vec<#inner_ty>, _>>()?;
1834 }
1835 } else if is_option_type(ty) {
1836 let inner_ty = option_inner_type(ty)
1837 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1838 quote! {
1839 let #ident = if segments
1840 .iter()
1841 .any(|__seg| __seg.tag == <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG)
1842 {
1843 ::core::option::Option::Some(
1844 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(segments)?
1845 )
1846 } else {
1847 ::core::option::Option::None
1848 };
1849 }
1850 } else {
1851 quote! {
1852 let #ident = <#ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(segments)?;
1853 }
1854 })
1855 })
1856 .collect::<syn::Result<_>>()?;
1857
1858 let owned_body = quote! {
1859 #(#field_inits_owned)*
1860 ::core::result::Result::Ok(Self { #(#field_names),* })
1861 };
1862
1863 (body, owned_body, quote! {})
1864 };
1865
1866 Ok(quote! {
1867 impl ::edifact_rs::EdifactDeserialize for #name {
1868 fn edifact_deserialize(
1869 segments: &[::edifact_rs::Segment<'_>],
1870 ) -> ::core::result::Result<Self, ::edifact_rs::EdifactError> {
1871 #body
1872 }
1873
1874 fn edifact_deserialize_owned(
1875 segments: &[::edifact_rs::OwnedSegment],
1876 ) -> ::core::result::Result<Self, ::edifact_rs::EdifactError> {
1877 #owned_body
1878 }
1879 }
1880 #segment_tag_impl
1881 })
1882}
1883
1884#[cfg(test)]
1885mod tests {
1886 #[test]
1900 fn trybuild_ui() {
1901 if std::env::var_os("EDIFACT_UI_TESTS").is_none() {
1902 eprintln!(
1903 "skipping derive UI suite: set EDIFACT_UI_TESTS=1 to run it \
1904 (expectations are pinned to the MSRV toolchain)"
1905 );
1906 return;
1907 }
1908 let t = trybuild::TestCases::new();
1909 t.pass("tests/ui/pass_*.rs");
1910 t.compile_fail("tests/ui/fail_*.rs");
1911 }
1912}