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