1use proc_macro::TokenStream;
99use proc_macro2::TokenStream as TokenStream2;
100use quote::quote;
101use syn::{Data, DeriveInput, Field, Fields, Type, parse_macro_input, spanned::Spanned};
102
103#[proc_macro_derive(EdifactSerialize, attributes(edifact))]
106pub fn derive_edifact_serialize(input: TokenStream) -> TokenStream {
115 let input = parse_macro_input!(input as DeriveInput);
116 impl_serialize(&input)
117 .unwrap_or_else(|e| e.to_compile_error())
118 .into()
119}
120
121#[proc_macro_derive(EdifactDeserialize, attributes(edifact))]
122pub fn derive_edifact_deserialize(input: TokenStream) -> TokenStream {
131 let input = parse_macro_input!(input as DeriveInput);
132 impl_deserialize(&input)
133 .unwrap_or_else(|e| e.to_compile_error())
134 .into()
135}
136
137#[derive(Default)]
140struct StructAttrs {
141 segment: Option<String>,
143 qualifier: Option<String>,
145 qualifier_span: Option<proc_macro2::Span>,
146 qualifier_from: Option<u32>,
148 qualifier_from_span: Option<proc_macro2::Span>,
149}
150
151#[derive(Default)]
152struct FieldAttrs {
153 element: Option<u32>,
155 element_span: Option<proc_macro2::Span>,
156 component: Option<u32>,
158 component_span: Option<proc_macro2::Span>,
159 composite: bool,
161 composite_span: Option<proc_macro2::Span>,
162 group: bool,
164 group_span: Option<proc_macro2::Span>,
165 qualifier: Option<String>,
167 qualifier_span: Option<proc_macro2::Span>,
168 required: bool,
176 required_span: Option<proc_macro2::Span>,
177}
178
179fn parse_struct_attrs(input: &DeriveInput) -> syn::Result<StructAttrs> {
182 let mut out = StructAttrs::default();
183 for attr in &input.attrs {
184 if !attr.path().is_ident("edifact") {
185 continue;
186 }
187 attr.parse_nested_meta(|meta| {
188 if meta.path.is_ident("segment") {
189 let lit = meta.value()?.parse::<syn::LitStr>()?;
190 let tag = lit.value();
191 if tag.len() != 3 || !tag.bytes().all(|b| b.is_ascii_uppercase()) {
192 return Err(syn::Error::new(
193 lit.span(),
194 format!(
195 "segment tag must be exactly 3 ASCII uppercase letters; got {tag:?}"
196 ),
197 ));
198 }
199 out.segment = Some(tag);
200 } else if meta.path.is_ident("qualifier") {
201 out.qualifier = Some(meta.value()?.parse::<syn::LitStr>()?.value());
202 out.qualifier_span = Some(meta.path.span());
203 } else if meta.path.is_ident("qualifier_from") {
204 let idx: u32 = meta.value()?.parse::<syn::LitInt>()?.base10_parse()?;
205 out.qualifier_from = Some(idx);
206 out.qualifier_from_span = Some(meta.path.span());
207 } else {
208 return Err(meta.error("unknown struct-level `edifact` key; expected `segment`, `qualifier`, or `qualifier_from`"));
209 }
210 Ok(())
211 })?;
212 }
213 if (out.qualifier.is_some() || out.qualifier_from.is_some()) && out.segment.is_none() {
214 return Err(syn::Error::new(
215 out.qualifier_span
216 .or(out.qualifier_from_span)
217 .unwrap_or_else(|| input.span()),
218 "#[edifact(qualifier = ...)] / #[edifact(qualifier_from = ...)] require #[edifact(segment = ...)]",
219 ));
220 }
221 if out.qualifier.is_some() && out.qualifier_from.is_some() {
222 return Err(syn::Error::new(
223 out.qualifier_from_span
224 .or(out.qualifier_span)
225 .unwrap_or_else(|| input.span()),
226 "use either #[edifact(qualifier = ...)] or #[edifact(qualifier_from = ...)], not both",
227 ));
228 }
229 Ok(out)
230}
231
232fn parse_field_attrs(field: &Field) -> syn::Result<FieldAttrs> {
233 let mut out = FieldAttrs::default();
234 for attr in &field.attrs {
235 if !attr.path().is_ident("edifact") {
236 continue;
237 }
238 attr.parse_nested_meta(|meta| {
239 if meta.path.is_ident("element") {
240 out.element = Some(meta.value()?.parse::<syn::LitInt>()?.base10_parse()?);
241 out.element_span = Some(meta.path.span());
242 } else if meta.path.is_ident("component") {
243 out.component = Some(meta.value()?.parse::<syn::LitInt>()?.base10_parse()?);
244 out.component_span = Some(meta.path.span());
245 } else if meta.path.is_ident("composite") {
246 out.composite = true;
247 out.composite_span = Some(meta.path.span());
248 } else if meta.path.is_ident("group") {
249 out.group = true;
250 out.group_span = Some(meta.path.span());
251 } else if meta.path.is_ident("qualifier") {
252 out.qualifier = Some(meta.value()?.parse::<syn::LitStr>()?.value());
253 out.qualifier_span = Some(meta.path.span());
254 } else if meta.path.is_ident("required") {
255 out.required = true;
256 out.required_span = Some(meta.path.span());
257 } else {
258 return Err(meta.error("unknown field-level `edifact` key; expected `element`, `component`, `composite`, `group`, `qualifier`, or `required`"));
259 }
260 Ok(())
261 })?;
262 }
263 Ok(out)
264}
265
266fn is_option_type(ty: &Type) -> bool {
269 matches!(ty, Type::Path(p) if p.path.segments.last().is_some_and(|s| s.ident == "Option"))
270}
271
272fn is_vec_type(ty: &Type) -> bool {
273 matches!(ty, Type::Path(p) if p.path.segments.last().is_some_and(|s| s.ident == "Vec"))
274}
275
276fn is_string_type(ty: &Type) -> bool {
294 let Type::Path(p) = ty else { return false };
295 if p.path.is_ident("String") {
297 return true;
298 }
299 let segs = &p.path.segments;
301 segs.len() == 3
302 && (segs[0].ident == "std" || segs[0].ident == "alloc")
303 && segs[1].ident == "string"
304 && segs[2].ident == "String"
305}
306
307fn is_str_ref_type(ty: &Type) -> bool {
309 let Type::Reference(r) = ty else { return false };
310 matches!(r.elem.as_ref(), Type::Path(p) if p.path.is_ident("str"))
311}
312
313fn is_str_like(ty: &Type) -> bool {
316 is_string_type(ty) || is_str_ref_type(ty)
317}
318
319fn option_inner_type(ty: &Type) -> Option<&Type> {
320 let Type::Path(path) = ty else { return None };
321 let seg = path.path.segments.last()?;
322 if seg.ident != "Option" {
323 return None;
324 }
325 let syn::PathArguments::AngleBracketed(args) = &seg.arguments else {
326 return None;
327 };
328 let syn::GenericArgument::Type(inner) = args.args.first()? else {
329 return None;
330 };
331 Some(inner)
332}
333
334fn vec_inner_type(ty: &Type) -> Option<&Type> {
335 let Type::Path(path) = ty else { return None };
336 let seg = path.path.segments.last()?;
337 if seg.ident != "Vec" {
338 return None;
339 }
340 let syn::PathArguments::AngleBracketed(args) = &seg.arguments else {
341 return None;
342 };
343 let syn::GenericArgument::Type(inner) = args.args.first()? else {
344 return None;
345 };
346 Some(inner)
347}
348
349fn get_named_fields(input: &DeriveInput) -> syn::Result<&syn::FieldsNamed> {
352 if !input.generics.params.is_empty() {
353 return Err(syn::Error::new(
354 input.generics.params.span(),
355 "EdifactSerialize/EdifactDeserialize do not support generic structs",
356 ));
357 }
358 match &input.data {
359 Data::Struct(s) => match &s.fields {
360 Fields::Named(f) => Ok(f),
361 _ => Err(syn::Error::new(
362 input.span(),
363 "EdifactSerialize/EdifactDeserialize only support structs with named fields",
364 )),
365 },
366 _ => Err(syn::Error::new(
367 input.span(),
368 "EdifactSerialize/EdifactDeserialize only support structs",
369 )),
370 }
371}
372
373fn validate_field_attrs(
374 ident: &syn::Ident,
375 ty: &Type,
376 attrs: &FieldAttrs,
377 is_segment_struct: bool,
378) -> syn::Result<()> {
379 if attrs.group && !is_vec_type(ty) {
380 return Err(syn::Error::new(
381 attrs.group_span.unwrap_or_else(|| ident.span()),
382 format!("field `{ident}`: #[edifact(group)] requires Vec<T>"),
383 ));
384 }
385 if attrs.group && (attrs.element.is_some() || attrs.component.is_some()) {
386 return Err(syn::Error::new(
387 attrs.group_span.unwrap_or_else(|| ident.span()),
388 format!(
389 "field `{ident}`: #[edifact(group)] cannot be combined with element/component positioning"
390 ),
391 ));
392 }
393 if attrs.composite && attrs.component.is_some() {
394 return Err(syn::Error::new(
395 attrs.component_span.unwrap_or_else(|| ident.span()),
396 format!(
397 "field `{ident}`: #[edifact(component = ...)] cannot be combined with #[edifact(composite)]"
398 ),
399 ));
400 }
401 if attrs.composite && attrs.group {
402 return Err(syn::Error::new(
403 attrs.composite_span.unwrap_or_else(|| ident.span()),
404 format!(
405 "field `{ident}`: #[edifact(composite)] cannot be combined with #[edifact(group)]"
406 ),
407 ));
408 }
409 if is_segment_struct && attrs.group {
410 return Err(syn::Error::new(
411 attrs.group_span.unwrap_or_else(|| ident.span()),
412 format!("field `{ident}`: #[edifact(group)] is only valid on message structs"),
413 ));
414 }
415 if !is_segment_struct && (attrs.element.is_some() || attrs.component.is_some()) {
416 return Err(syn::Error::new(
417 attrs
418 .element_span
419 .or(attrs.component_span)
420 .unwrap_or_else(|| ident.span()),
421 format!(
422 "field `{ident}`: element/component positioning is only valid on segment structs"
423 ),
424 ));
425 }
426 if !is_segment_struct && attrs.composite {
427 return Err(syn::Error::new(
428 attrs.composite_span.unwrap_or_else(|| ident.span()),
429 format!("field `{ident}`: #[edifact(composite)] is only valid on segment structs"),
430 ));
431 }
432 if is_segment_struct && attrs.qualifier.is_some() {
433 return Err(syn::Error::new(
434 attrs.qualifier_span.unwrap_or_else(|| ident.span()),
435 format!(
436 "field `{ident}`: #[edifact(qualifier = ...)] is only valid on message struct fields"
437 ),
438 ));
439 }
440 if attrs.qualifier.is_some() && attrs.group && !is_vec_type(ty) {
441 return Err(syn::Error::new(
442 attrs.qualifier_span.unwrap_or_else(|| ident.span()),
443 format!("field `{ident}`: qualifier-constrained groups must be Vec<T>"),
444 ));
445 }
446 if attrs.required && !is_option_type(ty) {
447 return Err(syn::Error::new(
448 attrs.required_span.unwrap_or_else(|| ident.span()),
449 format!(
450 "field `{ident}`: #[edifact(required)] only applies to Option<T> fields; \
451 non-Option fields are always required"
452 ),
453 ));
454 }
455 if attrs.required && attrs.composite {
456 return Err(syn::Error::new(
457 attrs.required_span.unwrap_or_else(|| ident.span()),
458 format!(
459 "field `{ident}`: #[edifact(required)] cannot be combined with \
460 #[edifact(composite)]; use a non-optional field type to require the \
461 composite element"
462 ),
463 ));
464 }
465 if attrs.required && !is_segment_struct {
466 return Err(syn::Error::new(
467 attrs.required_span.unwrap_or_else(|| ident.span()),
468 format!(
469 "field `{ident}`: #[edifact(required)] is only valid on segment struct \
470 element fields; to require a segment in a message struct, use a \
471 non-optional field type"
472 ),
473 ));
474 }
475 Ok(())
476}
477
478fn impl_serialize(input: &DeriveInput) -> syn::Result<TokenStream2> {
481 let name = &input.ident;
482 let struct_attrs = parse_struct_attrs(input)?;
483 let fields = get_named_fields(input)?;
484 let is_segment_struct = struct_attrs.segment.is_some();
485
486 let field_data: Vec<(&syn::Ident, &Type, FieldAttrs)> = fields
488 .named
489 .iter()
490 .map(|f| {
491 let attrs = parse_field_attrs(f)?;
492 let ident = f
493 .ident
494 .as_ref()
495 .ok_or_else(|| syn::Error::new_spanned(f, "only named fields are supported"))?;
496 validate_field_attrs(ident, &f.ty, &attrs, is_segment_struct)?;
497 Ok((ident, &f.ty, attrs))
498 })
499 .collect::<syn::Result<_>>()?;
500
501 let body = if let Some(seg_tag) = &struct_attrs.segment {
502 let (qualifier_emit, start_slot, elem0_comp_stmts) = if let Some(qual) =
507 &struct_attrs.qualifier
508 {
509 for (i, (ident, _, attrs)) in field_data.iter().enumerate() {
511 let elem = attrs.element.unwrap_or(i as u32);
512 let comp = attrs.component.unwrap_or(0);
513 if elem == 0 && comp == 0 {
514 return Err(syn::Error::new(
515 attrs
516 .element_span
517 .or(attrs.component_span)
518 .unwrap_or_else(|| ident.span()),
519 format!(
520 "field `{}`: cannot use #[edifact(qualifier = ...)] with a field at element = 0 without component >= 1; the qualifier occupies component 0",
521 ident
522 ),
523 ));
524 }
525 }
526 let mut comp_fields: Vec<(u32, usize)> = field_data
528 .iter()
529 .enumerate()
530 .filter_map(|(i, (_, _, attrs))| {
531 let elem = attrs.element.unwrap_or(i as u32);
532 let comp = attrs.component.unwrap_or(0);
533 if elem == 0 && comp > 0 {
534 Some((comp, i))
535 } else {
536 None
537 }
538 })
539 .collect();
540 comp_fields.sort_by_key(|(c, _)| *c);
541 let comp_stmts: Vec<TokenStream2> = comp_fields
542 .iter()
543 .map(|(_, fi)| {
544 let (ident, ty, _) = &field_data[*fi];
545 emit_component_element(ident, ty)
546 })
547 .collect();
548 let q = quote! {
549 emitter.emit(::edifact_rs::EdifactEvent::Element { value: #qual })?;
550 };
551 (q, 1u32, quote! { #(#comp_stmts)* })
552 } else {
553 (quote! {}, 0u32, quote! {})
554 };
555
556 let regular_field_data: Vec<(u32, usize)> = field_data
558 .iter()
559 .enumerate()
560 .filter_map(|(i, (_, _, attrs))| {
561 let elem = attrs.element.unwrap_or(i as u32);
562 if elem < start_slot {
563 None
564 } else {
565 Some((elem, i))
566 }
567 })
568 .collect();
569 let reg_max_idx = regular_field_data
570 .iter()
571 .map(|(e, _)| *e)
572 .max()
573 .unwrap_or(start_slot.saturating_sub(1));
574 let reg_field_map: std::collections::HashMap<u32, usize> =
575 regular_field_data.iter().copied().collect();
576
577 let mut elem_stmts: Vec<TokenStream2> = Vec::new();
578 for slot in start_slot..=reg_max_idx {
579 if let Some(&fi) = reg_field_map.get(&slot) {
580 let (ident, ty, attrs) = &field_data[fi];
581 if attrs.composite {
582 elem_stmts.push(emit_composite_field(ident, ty));
583 } else {
584 elem_stmts.push(emit_element(ident, ty));
585 }
586 } else {
587 elem_stmts.push(quote! {
589 emitter.emit(::edifact_rs::EdifactEvent::Element { value: "" })?;
590 });
591 }
592 }
593
594 quote! {
595 emitter.emit(::edifact_rs::EdifactEvent::StartSegment { tag: #seg_tag })?;
596 #qualifier_emit
597 #elem0_comp_stmts
598 #(#elem_stmts)*
599 emitter.emit(::edifact_rs::EdifactEvent::EndSegment)?;
600 }
601 } else {
602 let stmts: Vec<TokenStream2> = field_data
604 .iter()
605 .map(|(ident, ty, attrs)| {
606 if attrs.group || is_vec_type(ty) {
607 quote! {
608 for __item in &self.#ident {
609 ::edifact_rs::EdifactSerialize::edifact_serialize(__item, emitter)?;
610 }
611 }
612 } else {
613 quote! {
614 ::edifact_rs::EdifactSerialize::edifact_serialize(&self.#ident, emitter)?;
615 }
616 }
617 })
618 .collect();
619 quote! { #(#stmts)* }
620 };
621
622 Ok(quote! {
623 impl ::edifact_rs::EdifactSerialize for #name {
624 fn edifact_serialize<__E: ::edifact_rs::EventEmitter>(
625 &self,
626 emitter: &mut __E,
627 ) -> ::core::result::Result<(), ::edifact_rs::EdifactError> {
628 #body
629 ::core::result::Result::Ok(())
630 }
631 }
632 })
633}
634
635fn emit_element(ident: &syn::Ident, ty: &Type) -> TokenStream2 {
640 if is_option_type(ty) {
641 let inner_is_str = option_inner_type(ty).is_some_and(is_str_like);
642 if inner_is_str {
643 quote! {
644 match &self.#ident {
645 ::core::option::Option::Some(__v) => {
646 emitter.emit(::edifact_rs::EdifactEvent::Element { value: __v.as_str() })?;
647 }
648 ::core::option::Option::None => {
649 emitter.emit(::edifact_rs::EdifactEvent::Element { value: "" })?;
650 }
651 }
652 }
653 } else {
654 quote! {
655 match &self.#ident {
656 ::core::option::Option::Some(__v) => {
657 let __s = ::std::string::ToString::to_string(__v);
658 emitter.emit(::edifact_rs::EdifactEvent::Element { value: &__s })?;
659 }
660 ::core::option::Option::None => {
661 emitter.emit(::edifact_rs::EdifactEvent::Element { value: "" })?;
662 }
663 }
664 }
665 }
666 } else if is_string_type(ty) {
667 quote! {
668 emitter.emit(::edifact_rs::EdifactEvent::Element { value: self.#ident.as_str() })?;
669 }
670 } else if is_str_ref_type(ty) {
671 quote! {
672 emitter.emit(::edifact_rs::EdifactEvent::Element { value: self.#ident })?;
673 }
674 } else {
675 quote! {
676 {
677 let __s = ::std::string::ToString::to_string(&self.#ident);
678 emitter.emit(::edifact_rs::EdifactEvent::Element { value: &__s })?;
679 }
680 }
681 }
682}
683
684fn emit_component_element(ident: &syn::Ident, ty: &Type) -> TokenStream2 {
688 if is_option_type(ty) {
689 let inner_is_str = option_inner_type(ty).is_some_and(is_str_like);
690 if inner_is_str {
691 quote! {
692 match &self.#ident {
693 ::core::option::Option::Some(__v) => {
694 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: __v.as_str() })?;
695 }
696 ::core::option::Option::None => {
697 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: "" })?;
698 }
699 }
700 }
701 } else {
702 quote! {
703 match &self.#ident {
704 ::core::option::Option::Some(__v) => {
705 let __s = ::std::string::ToString::to_string(__v);
706 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: &__s })?;
707 }
708 ::core::option::Option::None => {
709 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: "" })?;
710 }
711 }
712 }
713 }
714 } else if is_string_type(ty) {
715 quote! {
716 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: self.#ident.as_str() })?;
717 }
718 } else if is_str_ref_type(ty) {
719 quote! {
720 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: self.#ident })?;
721 }
722 } else {
723 quote! {
724 {
725 let __s = ::std::string::ToString::to_string(&self.#ident);
726 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: &__s })?;
727 }
728 }
729 }
730}
731
732fn emit_composite_field(ident: &syn::Ident, ty: &Type) -> TokenStream2 {
734 if is_option_type(ty) {
735 quote! {
736 match &self.#ident {
737 ::core::option::Option::Some(__v) => {
738 ::edifact_rs::EdifactCompositeSerialize::edifact_serialize_composite(__v, emitter)?;
739 }
740 ::core::option::Option::None => {
741 emitter.emit(::edifact_rs::EdifactEvent::Element { value: "" })?;
742 }
743 }
744 }
745 } else {
746 quote! {
747 ::edifact_rs::EdifactCompositeSerialize::edifact_serialize_composite(&self.#ident, emitter)?;
748 }
749 }
750}
751
752fn impl_deserialize(input: &DeriveInput) -> syn::Result<TokenStream2> {
755 let name = &input.ident;
756 let struct_attrs = parse_struct_attrs(input)?;
757 let fields = get_named_fields(input)?;
758 let is_segment_struct = struct_attrs.segment.is_some();
759
760 let field_data: Vec<(&syn::Ident, &Type, FieldAttrs)> = fields
761 .named
762 .iter()
763 .map(|f| {
764 let attrs = parse_field_attrs(f)?;
765 let ident = f
766 .ident
767 .as_ref()
768 .ok_or_else(|| syn::Error::new_spanned(f, "only named fields are supported"))?;
769 validate_field_attrs(ident, &f.ty, &attrs, is_segment_struct)?;
770 Ok((ident, &f.ty, attrs))
771 })
772 .collect::<syn::Result<_>>()?;
773
774 let field_names: Vec<&syn::Ident> = field_data.iter().map(|(id, _, _)| *id).collect();
775
776 let (body, owned_body, segment_tag_impl) = if let Some(seg_tag) = &struct_attrs.segment {
777 let qualifier_guard = if let Some(qual) = &struct_attrs.qualifier {
779 quote! {
780 if __seg.element_str(0).unwrap_or("") != #qual {
781 return ::core::result::Result::Err(
782 ::edifact_rs::EdifactError::MissingRequiredElement {
783 tag: #seg_tag.to_owned(),
784 element_index: 0,
785 }
786 );
787 }
788 }
789 } else if let Some(idx) = struct_attrs.qualifier_from {
790 quote! {
791 match __seg.element_str(#idx as usize) {
792 None => return ::core::result::Result::Err(
793 ::edifact_rs::EdifactError::MissingRequiredElement {
794 tag: #seg_tag.to_owned(),
795 element_index: #idx as usize,
796 }
797 ),
798 Some("") => return ::core::result::Result::Err(
799 ::edifact_rs::EdifactError::InvalidFieldValue {
800 tag: #seg_tag.to_owned(),
801 element_index: #idx as usize,
802 value: ::std::string::String::new(),
803 }
804 ),
805 Some(__qual_val) => { let _ = __qual_val; }
806 }
807 }
808 } else {
809 quote! {}
810 };
811
812 let find_seg = if let Some(qual) = &struct_attrs.qualifier {
813 quote! {
814 ::edifact_rs::helpers::find_qualified_segment(segments, #seg_tag, #qual)
815 }
816 } else {
817 quote! {
818 ::edifact_rs::helpers::find_segment(segments, #seg_tag)
819 }
820 };
821
822 let field_inits: Vec<TokenStream2> = field_data
823 .iter()
824 .enumerate()
825 .map(|(decl_i, (ident, ty, attrs))| -> syn::Result<TokenStream2> {
826 let idx = attrs.element.unwrap_or(decl_i as u32) as usize;
827 if attrs.composite {
828 if is_option_type(ty) {
829 let inner_ty = option_inner_type(ty)
830 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
831 return Ok(quote! {
832 let #ident = match ::edifact_rs::helpers::composite_element(__seg, #idx) {
833 ::core::option::Option::Some(__composite) => {
834 ::core::option::Option::Some(
835 <#inner_ty as ::edifact_rs::EdifactCompositeDeserialize>::edifact_deserialize_composite(__composite)?
836 )
837 }
838 ::core::option::Option::None => ::core::option::Option::None,
839 };
840 });
841 }
842 return Ok(quote! {
843 let #ident = <#ty as ::edifact_rs::EdifactCompositeDeserialize>::edifact_deserialize_composite(
844 ::edifact_rs::helpers::composite_element(__seg, #idx).ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
845 tag: #seg_tag.to_owned(),
846 element_index: #idx as usize,
847 })?
848 )?;
849 });
850 }
851 let component_idx: Option<usize> = attrs.component.map(|c| c as usize);
852 let value_expr = if let Some(comp) = component_idx {
853 quote! {
854 __seg.get_element(#idx).and_then(|__e| __e.get_component(#comp))
855 }
856 } else {
857 quote! { __seg.element_str(#idx) }
858 };
859 let missing_required_err = if let Some(comp) = component_idx {
862 quote! {
863 ::edifact_rs::EdifactError::MissingRequiredComponent {
864 tag: #seg_tag.to_owned(),
865 element_index: #idx as usize,
866 component_index: #comp as usize,
867 }
868 }
869 } else {
870 quote! {
871 ::edifact_rs::EdifactError::MissingRequiredElement {
872 tag: #seg_tag.to_owned(),
873 element_index: #idx as usize,
874 }
875 }
876 };
877 Ok(if is_option_type(ty) {
878 let inner_ty = option_inner_type(ty);
879 let inner_is_str = inner_ty.is_some_and(is_str_like);
880 if attrs.required {
881 if inner_is_str {
885 quote! {
886 let #ident = ::core::option::Option::Some(
887 #value_expr
888 .filter(|__s| !__s.is_empty())
889 .ok_or_else(|| #missing_required_err)?
890 .to_owned()
891 );
892 }
893 } else {
894 let inner_ty = inner_ty
895 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
896 quote! {
897 let #ident = ::core::option::Option::Some(
898 #value_expr
899 .filter(|__s| !__s.is_empty())
900 .ok_or_else(|| #missing_required_err)?
901 .parse::<#inner_ty>()
902 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })?
903 );
904 }
905 }
906 } else if inner_is_str {
907 quote! {
908 let #ident = #value_expr
909 .filter(|__s| !__s.is_empty())
910 .map(::std::string::String::from);
911 }
912 } else {
913 let inner_ty = inner_ty
914 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
915 quote! {
916 let #ident = #value_expr
917 .filter(|__s| !__s.is_empty())
918 .map(|__s| __s.parse::<#inner_ty>()
919 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })
920 )
921 .transpose()?;
922 }
923 }
924 } else if is_str_like(ty) {
925 quote! {
926 let #ident = #value_expr
927 .filter(|__s| !__s.is_empty())
928 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
929 tag: #seg_tag.to_owned(),
930 element_index: #idx as usize,
931 })?
932 .to_owned();
933 }
934 } else {
935 quote! {
936 let #ident = #value_expr
937 .filter(|__s| !__s.is_empty())
938 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
939 tag: #seg_tag.to_owned(),
940 element_index: #idx as usize,
941 })?
942 .parse::<#ty>()
943 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })?;
944 }
945 })
946 })
947 .collect::<syn::Result<_>>()?;
948
949 let body = quote! {
950 let __seg = #find_seg
951 .ok_or_else(|| ::edifact_rs::EdifactError::MissingSegment {
952 tag: #seg_tag.to_owned(),
953 expected_position: "message body".to_owned(),
954 })?;
955 #qualifier_guard
956 #(#field_inits)*
957 ::core::result::Result::Ok(Self { #(#field_names),* })
958 };
959
960 let qualifier_match = if let Some(qual) = &struct_attrs.qualifier {
962 quote! {
963 fn matches_segment(seg: &::edifact_rs::Segment<'_>) -> bool {
964 seg.tag == Self::SEGMENT_TAG
965 && seg.element_str(0).unwrap_or("") == #qual
966 }
967 }
968 } else if let Some(idx) = struct_attrs.qualifier_from {
969 quote! {
970 fn matches_segment(seg: &::edifact_rs::Segment<'_>) -> bool {
971 seg.tag == Self::SEGMENT_TAG
972 && !seg.element_str(#idx as usize).unwrap_or("").is_empty()
973 }
974 }
975 } else {
976 quote! {}
977 };
978
979 let seg_tag_impl = quote! {
980 impl ::edifact_rs::EdifactSegmentTag for #name {
981 const SEGMENT_TAG: &'static str = #seg_tag;
982 #qualifier_match
983 }
984 };
985
986 let find_seg_owned = if let Some(qual) = &struct_attrs.qualifier {
989 quote! {
990 ::edifact_rs::helpers::find_qualified_segment_owned(segments, #seg_tag, #qual)
991 }
992 } else {
993 quote! {
994 ::edifact_rs::helpers::find_segment_owned(segments, #seg_tag)
995 }
996 };
997
998 let field_inits_owned: Vec<TokenStream2> = field_data
999 .iter()
1000 .enumerate()
1001 .map(|(decl_i, (ident, ty, attrs))| -> syn::Result<TokenStream2> {
1002 let idx = attrs.element.unwrap_or(decl_i as u32) as usize;
1003 if attrs.composite {
1004 if is_option_type(ty) {
1005 let inner_ty = option_inner_type(ty)
1006 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1007 return Ok(quote! {
1008 let #ident = match __seg.elements.get(#idx) {
1009 ::core::option::Option::Some(__e) => {
1010 let __cows = __e.components.iter()
1011 .map(|(s, _)| ::std::borrow::Cow::Borrowed(s.as_str()))
1012 .collect::<::std::vec::Vec<::std::borrow::Cow<'_, str>>>();
1013 ::core::option::Option::Some(
1014 <#inner_ty as ::edifact_rs::EdifactCompositeDeserialize>::edifact_deserialize_composite(
1015 ::edifact_rs::CompositeElement::from_slice(&__cows)
1016 )?
1017 )
1018 }
1019 ::core::option::Option::None => ::core::option::Option::None,
1020 };
1021 });
1022 }
1023 return Ok(quote! {
1024 let #ident = {
1025 let __cows = __seg.elements.get(#idx)
1026 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
1027 tag: #seg_tag.to_owned(),
1028 element_index: #idx as usize,
1029 })?
1030 .components.iter()
1031 .map(|(s, _)| ::std::borrow::Cow::Borrowed(s.as_str()))
1032 .collect::<::std::vec::Vec<::std::borrow::Cow<'_, str>>>();
1033 <#ty as ::edifact_rs::EdifactCompositeDeserialize>::edifact_deserialize_composite(
1034 ::edifact_rs::CompositeElement::from_slice(&__cows)
1035 )?
1036 };
1037 });
1038 }
1039 let component_idx_owned: Option<usize> = attrs.component.map(|c| c as usize);
1040 let value_expr_owned = if let Some(comp) = component_idx_owned {
1041 quote! { __seg.component_str(#idx, #comp) }
1042 } else {
1043 quote! { __seg.element_str(#idx) }
1044 };
1045 let missing_required_err_owned = if let Some(comp) = component_idx_owned {
1047 quote! {
1048 ::edifact_rs::EdifactError::MissingRequiredComponent {
1049 tag: #seg_tag.to_owned(),
1050 element_index: #idx as usize,
1051 component_index: #comp as usize,
1052 }
1053 }
1054 } else {
1055 quote! {
1056 ::edifact_rs::EdifactError::MissingRequiredElement {
1057 tag: #seg_tag.to_owned(),
1058 element_index: #idx as usize,
1059 }
1060 }
1061 };
1062 Ok(if is_option_type(ty) {
1063 if let Some(inner_ty) = option_inner_type(ty) {
1064 if attrs.required {
1065 if is_str_like(inner_ty) {
1069 quote! {
1070 let #ident = ::core::option::Option::Some(
1071 #value_expr_owned
1072 .filter(|__s| !__s.is_empty())
1073 .ok_or_else(|| #missing_required_err_owned)?
1074 .to_owned()
1075 );
1076 }
1077 } else {
1078 quote! {
1079 let #ident = ::core::option::Option::Some(
1080 #value_expr_owned
1081 .filter(|__s| !__s.is_empty())
1082 .ok_or_else(|| #missing_required_err_owned)?
1083 .parse::<#inner_ty>()
1084 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })?
1085 );
1086 }
1087 }
1088 } else if is_str_like(inner_ty) {
1089 quote! {
1090 let #ident = #value_expr_owned
1091 .filter(|__s| !__s.is_empty())
1092 .map(::std::string::String::from);
1093 }
1094 } else {
1095 quote! {
1096 let #ident = #value_expr_owned
1097 .filter(|__s| !__s.is_empty())
1098 .map(|__s| __s.parse::<#inner_ty>()
1099 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })
1100 )
1101 .transpose()?;
1102 }
1103 }
1104 } else {
1105 quote! {
1107 let #ident = #value_expr_owned
1108 .filter(|__s| !__s.is_empty())
1109 .map(::std::string::String::from);
1110 }
1111 }
1112 } else if is_str_like(ty) {
1113 quote! {
1114 let #ident = #value_expr_owned
1115 .filter(|__s| !__s.is_empty())
1116 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
1117 tag: #seg_tag.to_owned(),
1118 element_index: #idx as usize,
1119 })?
1120 .to_owned();
1121 }
1122 } else {
1123 quote! {
1124 let #ident = #value_expr_owned
1125 .filter(|__s| !__s.is_empty())
1126 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
1127 tag: #seg_tag.to_owned(),
1128 element_index: #idx as usize,
1129 })?
1130 .parse::<#ty>()
1131 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })?;
1132 }
1133 })
1134 })
1135 .collect::<syn::Result<_>>()?;
1136
1137 let owned_body = quote! {
1138 let __seg = #find_seg_owned
1139 .ok_or_else(|| ::edifact_rs::EdifactError::MissingSegment {
1140 tag: #seg_tag.to_owned(),
1141 expected_position: "message body".to_owned(),
1142 })?;
1143 #qualifier_guard
1144 #(#field_inits_owned)*
1145 ::core::result::Result::Ok(Self { #(#field_names),* })
1146 };
1147
1148 (body, owned_body, seg_tag_impl)
1149 } else {
1150 let field_inits: Vec<TokenStream2> = field_data
1152 .iter()
1153 .map(|(ident, ty, attrs)| -> syn::Result<TokenStream2> {
1154 Ok(if let Some(qual) = &attrs.qualifier {
1155 if attrs.group || is_vec_type(ty) {
1156 let inner_ty = vec_inner_type(ty)
1157 .ok_or_else(|| syn::Error::new(ident.span(), "expected Vec<T>"))?;
1158 quote! {
1159 let #ident = segments
1160 .iter()
1161 .filter(|__seg| {
1162 __seg.tag == <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG
1163 && __seg.element_str(0).unwrap_or("") == #qual
1164 })
1165 .map(|__seg| {
1166 ::edifact_rs::EdifactDeserialize::edifact_deserialize(
1167 ::core::slice::from_ref(__seg),
1168 )
1169 })
1170 .collect::<::core::result::Result<::std::vec::Vec<#inner_ty>, ::edifact_rs::EdifactError>>()?;
1171 }
1172 } else if is_option_type(ty) {
1173 let inner_ty = option_inner_type(ty)
1174 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1175 quote! {
1176 let #ident = match ::edifact_rs::helpers::find_qualified_segment(
1177 segments,
1178 <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG,
1179 #qual,
1180 ) {
1181 ::core::option::Option::Some(__seg) => {
1182 ::core::option::Option::Some(
1183 ::edifact_rs::EdifactDeserialize::edifact_deserialize(
1184 ::core::slice::from_ref(__seg),
1185 )?
1186 )
1187 }
1188 ::core::option::Option::None => ::core::option::Option::None,
1189 };
1190 }
1191 } else {
1192 quote! {
1193 let __seg = ::edifact_rs::helpers::find_qualified_segment(
1194 segments,
1195 <#ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG,
1196 #qual,
1197 )
1198 .ok_or_else(|| ::edifact_rs::EdifactError::MissingSegment {
1199 tag: <#ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG.to_owned(),
1200 expected_position: "message body".to_owned(),
1201 })?;
1202 let #ident = ::edifact_rs::EdifactDeserialize::edifact_deserialize(
1203 ::core::slice::from_ref(__seg),
1204 )?;
1205 }
1206 }
1207 } else if attrs.group || is_vec_type(ty) {
1208 let inner_ty = vec_inner_type(ty)
1209 .ok_or_else(|| syn::Error::new(ident.span(), "expected Vec<T>"))?;
1210 quote! {
1211 let #ident = ::edifact_rs::helpers::find_segments_typed::<#inner_ty>(segments)
1212 .map(|__seg| {
1213 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize(
1214 ::core::slice::from_ref(__seg),
1215 )
1216 })
1217 .collect::<::core::result::Result<::std::vec::Vec<#inner_ty>, _>>()?;
1218 }
1219 } else if is_option_type(ty) {
1220 let inner_ty = option_inner_type(ty)
1221 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1222 quote! {
1223 let #ident = if segments
1224 .iter()
1225 .any(|__seg| __seg.tag == <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG)
1226 {
1227 ::core::option::Option::Some(
1228 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize(segments)?
1229 )
1230 } else {
1231 ::core::option::Option::None
1232 };
1233 }
1234 } else {
1235 quote! {
1236 let #ident = ::edifact_rs::EdifactDeserialize::edifact_deserialize(segments)?;
1237 }
1238 })
1239 })
1240 .collect::<syn::Result<_>>()?;
1241
1242 let body = quote! {
1243 #(#field_inits)*
1244 ::core::result::Result::Ok(Self { #(#field_names),* })
1245 };
1246
1247 let field_inits_owned: Vec<TokenStream2> = field_data
1250 .iter()
1251 .map(|(ident, ty, attrs)| -> syn::Result<TokenStream2> {
1252 Ok(if let Some(qual) = &attrs.qualifier {
1253 if attrs.group || is_vec_type(ty) {
1254 let inner_ty = vec_inner_type(ty)
1255 .ok_or_else(|| syn::Error::new(ident.span(), "expected Vec<T>"))?;
1256 quote! {
1257 let #ident = segments
1258 .iter()
1259 .filter(|__seg| {
1260 __seg.tag == <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG
1261 && __seg.element_str(0).unwrap_or("") == #qual
1262 })
1263 .map(|__seg| {
1264 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(
1265 ::core::slice::from_ref(__seg),
1266 )
1267 })
1268 .collect::<::core::result::Result<::std::vec::Vec<#inner_ty>, ::edifact_rs::EdifactError>>()?;
1269 }
1270 } else if is_option_type(ty) {
1271 let inner_ty = option_inner_type(ty)
1272 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1273 quote! {
1274 let #ident = match ::edifact_rs::helpers::find_qualified_segment_owned(
1275 segments,
1276 <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG,
1277 #qual,
1278 ) {
1279 ::core::option::Option::Some(__seg) => {
1280 ::core::option::Option::Some(
1281 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(
1282 ::core::slice::from_ref(__seg),
1283 )?
1284 )
1285 }
1286 ::core::option::Option::None => ::core::option::Option::None,
1287 };
1288 }
1289 } else {
1290 quote! {
1291 let __seg = ::edifact_rs::helpers::find_qualified_segment_owned(
1292 segments,
1293 <#ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG,
1294 #qual,
1295 )
1296 .ok_or_else(|| ::edifact_rs::EdifactError::MissingSegment {
1297 tag: <#ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG.to_owned(),
1298 expected_position: "message body".to_owned(),
1299 })?;
1300 let #ident = <#ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(
1301 ::core::slice::from_ref(__seg),
1302 )?;
1303 }
1304 }
1305 } else if attrs.group || is_vec_type(ty) {
1306 let inner_ty = vec_inner_type(ty)
1307 .ok_or_else(|| syn::Error::new(ident.span(), "expected Vec<T>"))?;
1308 quote! {
1309 let #ident = segments
1310 .iter()
1311 .filter(|__seg| <#inner_ty as ::edifact_rs::EdifactSegmentTag>::matches_owned_segment(__seg))
1312 .map(|__seg| {
1313 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(
1314 ::core::slice::from_ref(__seg),
1315 )
1316 })
1317 .collect::<::core::result::Result<::std::vec::Vec<#inner_ty>, _>>()?;
1318 }
1319 } else if is_option_type(ty) {
1320 let inner_ty = option_inner_type(ty)
1321 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1322 quote! {
1323 let #ident = if segments
1324 .iter()
1325 .any(|__seg| __seg.tag == <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG)
1326 {
1327 ::core::option::Option::Some(
1328 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(segments)?
1329 )
1330 } else {
1331 ::core::option::Option::None
1332 };
1333 }
1334 } else {
1335 quote! {
1336 let #ident = <#ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(segments)?;
1337 }
1338 })
1339 })
1340 .collect::<syn::Result<_>>()?;
1341
1342 let owned_body = quote! {
1343 #(#field_inits_owned)*
1344 ::core::result::Result::Ok(Self { #(#field_names),* })
1345 };
1346
1347 (body, owned_body, quote! {})
1348 };
1349
1350 Ok(quote! {
1351 impl ::edifact_rs::EdifactDeserialize for #name {
1352 fn edifact_deserialize(
1353 segments: &[::edifact_rs::Segment<'_>],
1354 ) -> ::core::result::Result<Self, ::edifact_rs::EdifactError> {
1355 #body
1356 }
1357
1358 fn edifact_deserialize_owned(
1359 segments: &[::edifact_rs::OwnedSegment],
1360 ) -> ::core::result::Result<Self, ::edifact_rs::EdifactError> {
1361 #owned_body
1362 }
1363 }
1364 #segment_tag_impl
1365 })
1366}
1367
1368#[cfg(test)]
1369mod tests {
1370 #[test]
1371 fn trybuild_ui() {
1372 let t = trybuild::TestCases::new();
1373 t.pass("tests/ui/pass_*.rs");
1374 t.compile_fail("tests/ui/fail_*.rs");
1375 }
1376}