1use proc_macro::TokenStream;
76use proc_macro2::TokenStream as TokenStream2;
77use quote::quote;
78use syn::{Data, DeriveInput, Field, Fields, Type, parse_macro_input, spanned::Spanned};
79
80#[proc_macro_derive(EdifactSerialize, attributes(edifact))]
83pub fn derive_edifact_serialize(input: TokenStream) -> TokenStream {
92 let input = parse_macro_input!(input as DeriveInput);
93 impl_serialize(&input)
94 .unwrap_or_else(|e| e.to_compile_error())
95 .into()
96}
97
98#[proc_macro_derive(EdifactDeserialize, attributes(edifact))]
99pub fn derive_edifact_deserialize(input: TokenStream) -> TokenStream {
108 let input = parse_macro_input!(input as DeriveInput);
109 impl_deserialize(&input)
110 .unwrap_or_else(|e| e.to_compile_error())
111 .into()
112}
113
114#[derive(Default)]
117struct StructAttrs {
118 segment: Option<String>,
120 qualifier: Option<String>,
122 qualifier_span: Option<proc_macro2::Span>,
123 qualifier_from: Option<u32>,
125 qualifier_from_span: Option<proc_macro2::Span>,
126}
127
128#[derive(Default)]
129struct FieldAttrs {
130 element: Option<u32>,
132 element_span: Option<proc_macro2::Span>,
133 component: Option<u32>,
135 component_span: Option<proc_macro2::Span>,
136 composite: bool,
138 composite_span: Option<proc_macro2::Span>,
139 group: bool,
141 group_span: Option<proc_macro2::Span>,
142 qualifier: Option<String>,
144 qualifier_span: Option<proc_macro2::Span>,
145}
146
147fn parse_struct_attrs(input: &DeriveInput) -> syn::Result<StructAttrs> {
150 let mut out = StructAttrs::default();
151 for attr in &input.attrs {
152 if !attr.path().is_ident("edifact") {
153 continue;
154 }
155 attr.parse_nested_meta(|meta| {
156 if meta.path.is_ident("segment") {
157 let lit = meta.value()?.parse::<syn::LitStr>()?;
158 let tag = lit.value();
159 if tag.len() != 3 || !tag.bytes().all(|b| b.is_ascii_uppercase()) {
160 return Err(syn::Error::new(
161 lit.span(),
162 format!(
163 "segment tag must be exactly 3 ASCII uppercase letters; got {tag:?}"
164 ),
165 ));
166 }
167 out.segment = Some(tag);
168 } else if meta.path.is_ident("qualifier") {
169 out.qualifier = Some(meta.value()?.parse::<syn::LitStr>()?.value());
170 out.qualifier_span = Some(meta.path.span());
171 } else if meta.path.is_ident("qualifier_from") {
172 let idx: u32 = meta.value()?.parse::<syn::LitInt>()?.base10_parse()?;
173 out.qualifier_from = Some(idx);
174 out.qualifier_from_span = Some(meta.path.span());
175 } else {
176 return Err(meta.error("unknown struct-level `edifact` key; expected `segment`, `qualifier`, or `qualifier_from`"));
177 }
178 Ok(())
179 })?;
180 }
181 if (out.qualifier.is_some() || out.qualifier_from.is_some()) && out.segment.is_none() {
182 return Err(syn::Error::new(
183 out.qualifier_span
184 .or(out.qualifier_from_span)
185 .unwrap_or_else(|| input.span()),
186 "#[edifact(qualifier = ...)] / #[edifact(qualifier_from = ...)] require #[edifact(segment = ...)]",
187 ));
188 }
189 if out.qualifier.is_some() && out.qualifier_from.is_some() {
190 return Err(syn::Error::new(
191 out.qualifier_from_span
192 .or(out.qualifier_span)
193 .unwrap_or_else(|| input.span()),
194 "use either #[edifact(qualifier = ...)] or #[edifact(qualifier_from = ...)], not both",
195 ));
196 }
197 Ok(out)
198}
199
200fn parse_field_attrs(field: &Field) -> syn::Result<FieldAttrs> {
201 let mut out = FieldAttrs::default();
202 for attr in &field.attrs {
203 if !attr.path().is_ident("edifact") {
204 continue;
205 }
206 attr.parse_nested_meta(|meta| {
207 if meta.path.is_ident("element") {
208 out.element = Some(meta.value()?.parse::<syn::LitInt>()?.base10_parse()?);
209 out.element_span = Some(meta.path.span());
210 } else if meta.path.is_ident("component") {
211 out.component = Some(meta.value()?.parse::<syn::LitInt>()?.base10_parse()?);
212 out.component_span = Some(meta.path.span());
213 } else if meta.path.is_ident("composite") {
214 out.composite = true;
215 out.composite_span = Some(meta.path.span());
216 } else if meta.path.is_ident("group") {
217 out.group = true;
218 out.group_span = Some(meta.path.span());
219 } else if meta.path.is_ident("qualifier") {
220 out.qualifier = Some(meta.value()?.parse::<syn::LitStr>()?.value());
221 out.qualifier_span = Some(meta.path.span());
222 } else {
223 return Err(meta.error("unknown field-level `edifact` key; expected `element`, `component`, `composite`, `group`, or `qualifier`"));
224 }
225 Ok(())
226 })?;
227 }
228 Ok(out)
229}
230
231fn is_option_type(ty: &Type) -> bool {
234 matches!(ty, Type::Path(p) if p.path.segments.last().is_some_and(|s| s.ident == "Option"))
235}
236
237fn is_vec_type(ty: &Type) -> bool {
238 matches!(ty, Type::Path(p) if p.path.segments.last().is_some_and(|s| s.ident == "Vec"))
239}
240
241fn is_string_type(ty: &Type) -> bool {
253 matches!(ty, Type::Path(p) if p.path.is_ident("String")
254 || p.path.segments.last().is_some_and(|s| s.ident == "String"))
255}
256
257fn is_str_ref_type(ty: &Type) -> bool {
259 let Type::Reference(r) = ty else { return false };
260 matches!(r.elem.as_ref(), Type::Path(p) if p.path.is_ident("str"))
261}
262
263fn is_str_like(ty: &Type) -> bool {
266 is_string_type(ty) || is_str_ref_type(ty)
267}
268
269fn option_inner_type(ty: &Type) -> Option<&Type> {
270 let Type::Path(path) = ty else { return None };
271 let seg = path.path.segments.last()?;
272 if seg.ident != "Option" {
273 return None;
274 }
275 let syn::PathArguments::AngleBracketed(args) = &seg.arguments else {
276 return None;
277 };
278 let syn::GenericArgument::Type(inner) = args.args.first()? else {
279 return None;
280 };
281 Some(inner)
282}
283
284fn vec_inner_type(ty: &Type) -> Option<&Type> {
285 let Type::Path(path) = ty else { return None };
286 let seg = path.path.segments.last()?;
287 if seg.ident != "Vec" {
288 return None;
289 }
290 let syn::PathArguments::AngleBracketed(args) = &seg.arguments else {
291 return None;
292 };
293 let syn::GenericArgument::Type(inner) = args.args.first()? else {
294 return None;
295 };
296 Some(inner)
297}
298
299fn get_named_fields(input: &DeriveInput) -> syn::Result<&syn::FieldsNamed> {
302 if !input.generics.params.is_empty() {
303 return Err(syn::Error::new(
304 input.generics.params.span(),
305 "EdifactSerialize/EdifactDeserialize do not support generic structs",
306 ));
307 }
308 match &input.data {
309 Data::Struct(s) => match &s.fields {
310 Fields::Named(f) => Ok(f),
311 _ => Err(syn::Error::new(
312 input.span(),
313 "EdifactSerialize/EdifactDeserialize only support structs with named fields",
314 )),
315 },
316 _ => Err(syn::Error::new(
317 input.span(),
318 "EdifactSerialize/EdifactDeserialize only support structs",
319 )),
320 }
321}
322
323fn validate_field_attrs(
324 ident: &syn::Ident,
325 ty: &Type,
326 attrs: &FieldAttrs,
327 is_segment_struct: bool,
328) -> syn::Result<()> {
329 if attrs.group && !is_vec_type(ty) {
330 return Err(syn::Error::new(
331 attrs.group_span.unwrap_or_else(|| ident.span()),
332 format!("field `{ident}`: #[edifact(group)] requires Vec<T>"),
333 ));
334 }
335 if attrs.group && (attrs.element.is_some() || attrs.component.is_some()) {
336 return Err(syn::Error::new(
337 attrs.group_span.unwrap_or_else(|| ident.span()),
338 format!(
339 "field `{ident}`: #[edifact(group)] cannot be combined with element/component positioning"
340 ),
341 ));
342 }
343 if attrs.composite && attrs.component.is_some() {
344 return Err(syn::Error::new(
345 attrs.component_span.unwrap_or_else(|| ident.span()),
346 format!(
347 "field `{ident}`: #[edifact(component = ...)] cannot be combined with #[edifact(composite)]"
348 ),
349 ));
350 }
351 if attrs.composite && attrs.group {
352 return Err(syn::Error::new(
353 attrs.composite_span.unwrap_or_else(|| ident.span()),
354 format!(
355 "field `{ident}`: #[edifact(composite)] cannot be combined with #[edifact(group)]"
356 ),
357 ));
358 }
359 if is_segment_struct && attrs.group {
360 return Err(syn::Error::new(
361 attrs.group_span.unwrap_or_else(|| ident.span()),
362 format!("field `{ident}`: #[edifact(group)] is only valid on message structs"),
363 ));
364 }
365 if !is_segment_struct && (attrs.element.is_some() || attrs.component.is_some()) {
366 return Err(syn::Error::new(
367 attrs
368 .element_span
369 .or(attrs.component_span)
370 .unwrap_or_else(|| ident.span()),
371 format!(
372 "field `{ident}`: element/component positioning is only valid on segment structs"
373 ),
374 ));
375 }
376 if !is_segment_struct && attrs.composite {
377 return Err(syn::Error::new(
378 attrs.composite_span.unwrap_or_else(|| ident.span()),
379 format!("field `{ident}`: #[edifact(composite)] is only valid on segment structs"),
380 ));
381 }
382 if is_segment_struct && attrs.qualifier.is_some() {
383 return Err(syn::Error::new(
384 attrs.qualifier_span.unwrap_or_else(|| ident.span()),
385 format!(
386 "field `{ident}`: #[edifact(qualifier = ...)] is only valid on message struct fields"
387 ),
388 ));
389 }
390 if attrs.qualifier.is_some() && attrs.group && !is_vec_type(ty) {
391 return Err(syn::Error::new(
392 attrs.qualifier_span.unwrap_or_else(|| ident.span()),
393 format!("field `{ident}`: qualifier-constrained groups must be Vec<T>"),
394 ));
395 }
396 Ok(())
397}
398
399fn impl_serialize(input: &DeriveInput) -> syn::Result<TokenStream2> {
402 let name = &input.ident;
403 let struct_attrs = parse_struct_attrs(input)?;
404 let fields = get_named_fields(input)?;
405 let is_segment_struct = struct_attrs.segment.is_some();
406
407 let field_data: Vec<(&syn::Ident, &Type, FieldAttrs)> = fields
409 .named
410 .iter()
411 .map(|f| {
412 let attrs = parse_field_attrs(f)?;
413 let ident = f
414 .ident
415 .as_ref()
416 .ok_or_else(|| syn::Error::new_spanned(f, "only named fields are supported"))?;
417 validate_field_attrs(ident, &f.ty, &attrs, is_segment_struct)?;
418 Ok((ident, &f.ty, attrs))
419 })
420 .collect::<syn::Result<_>>()?;
421
422 let body = if let Some(seg_tag) = &struct_attrs.segment {
423 let (qualifier_emit, start_slot, elem0_comp_stmts) = if let Some(qual) =
428 &struct_attrs.qualifier
429 {
430 for (i, (ident, _, attrs)) in field_data.iter().enumerate() {
432 let elem = attrs.element.unwrap_or(i as u32);
433 let comp = attrs.component.unwrap_or(0);
434 if elem == 0 && comp == 0 {
435 return Err(syn::Error::new(
436 attrs
437 .element_span
438 .or(attrs.component_span)
439 .unwrap_or_else(|| ident.span()),
440 format!(
441 "field `{}`: cannot use #[edifact(qualifier = ...)] with a field at element = 0 without component >= 1; the qualifier occupies component 0",
442 ident
443 ),
444 ));
445 }
446 }
447 let mut comp_fields: Vec<(u32, usize)> = field_data
449 .iter()
450 .enumerate()
451 .filter_map(|(i, (_, _, attrs))| {
452 let elem = attrs.element.unwrap_or(i as u32);
453 let comp = attrs.component.unwrap_or(0);
454 if elem == 0 && comp > 0 {
455 Some((comp, i))
456 } else {
457 None
458 }
459 })
460 .collect();
461 comp_fields.sort_by_key(|(c, _)| *c);
462 let comp_stmts: Vec<TokenStream2> = comp_fields
463 .iter()
464 .map(|(_, fi)| {
465 let (ident, ty, _) = &field_data[*fi];
466 emit_component_element(ident, ty)
467 })
468 .collect();
469 let q = quote! {
470 emitter.emit(::edifact_rs::EdifactEvent::Element { value: #qual })?;
471 };
472 (q, 1u32, quote! { #(#comp_stmts)* })
473 } else {
474 (quote! {}, 0u32, quote! {})
475 };
476
477 let regular_field_data: Vec<(u32, usize)> = field_data
479 .iter()
480 .enumerate()
481 .filter_map(|(i, (_, _, attrs))| {
482 let elem = attrs.element.unwrap_or(i as u32);
483 if elem < start_slot {
484 None
485 } else {
486 Some((elem, i))
487 }
488 })
489 .collect();
490 let reg_max_idx = regular_field_data
491 .iter()
492 .map(|(e, _)| *e)
493 .max()
494 .unwrap_or(start_slot.saturating_sub(1));
495 let reg_field_map: std::collections::HashMap<u32, usize> =
496 regular_field_data.iter().copied().collect();
497
498 let mut elem_stmts: Vec<TokenStream2> = Vec::new();
499 for slot in start_slot..=reg_max_idx {
500 if let Some(&fi) = reg_field_map.get(&slot) {
501 let (ident, ty, attrs) = &field_data[fi];
502 if attrs.composite {
503 elem_stmts.push(emit_composite_field(ident, ty));
504 } else {
505 elem_stmts.push(emit_element(ident, ty));
506 }
507 } else {
508 elem_stmts.push(quote! {
510 emitter.emit(::edifact_rs::EdifactEvent::Element { value: "" })?;
511 });
512 }
513 }
514
515 quote! {
516 emitter.emit(::edifact_rs::EdifactEvent::StartSegment { tag: #seg_tag })?;
517 #qualifier_emit
518 #elem0_comp_stmts
519 #(#elem_stmts)*
520 emitter.emit(::edifact_rs::EdifactEvent::EndSegment)?;
521 }
522 } else {
523 let stmts: Vec<TokenStream2> = field_data
525 .iter()
526 .map(|(ident, ty, attrs)| {
527 if attrs.group || is_vec_type(ty) {
528 quote! {
529 for __item in &self.#ident {
530 ::edifact_rs::EdifactSerialize::edifact_serialize(__item, emitter)?;
531 }
532 }
533 } else {
534 quote! {
535 ::edifact_rs::EdifactSerialize::edifact_serialize(&self.#ident, emitter)?;
536 }
537 }
538 })
539 .collect();
540 quote! { #(#stmts)* }
541 };
542
543 Ok(quote! {
544 impl ::edifact_rs::EdifactSerialize for #name {
545 fn edifact_serialize<__E: ::edifact_rs::EventEmitter>(
546 &self,
547 emitter: &mut __E,
548 ) -> ::core::result::Result<(), ::edifact_rs::EdifactError> {
549 #body
550 ::core::result::Result::Ok(())
551 }
552 }
553 })
554}
555
556fn emit_element(ident: &syn::Ident, ty: &Type) -> TokenStream2 {
561 if is_option_type(ty) {
562 let inner_is_str = option_inner_type(ty).is_some_and(is_str_like);
563 if inner_is_str {
564 quote! {
565 match &self.#ident {
566 ::core::option::Option::Some(__v) => {
567 emitter.emit(::edifact_rs::EdifactEvent::Element { value: __v.as_str() })?;
568 }
569 ::core::option::Option::None => {
570 emitter.emit(::edifact_rs::EdifactEvent::Element { value: "" })?;
571 }
572 }
573 }
574 } else {
575 quote! {
576 match &self.#ident {
577 ::core::option::Option::Some(__v) => {
578 let __s = ::std::string::ToString::to_string(__v);
579 emitter.emit(::edifact_rs::EdifactEvent::Element { value: &__s })?;
580 }
581 ::core::option::Option::None => {
582 emitter.emit(::edifact_rs::EdifactEvent::Element { value: "" })?;
583 }
584 }
585 }
586 }
587 } else if is_string_type(ty) {
588 quote! {
589 emitter.emit(::edifact_rs::EdifactEvent::Element { value: self.#ident.as_str() })?;
590 }
591 } else if is_str_ref_type(ty) {
592 quote! {
593 emitter.emit(::edifact_rs::EdifactEvent::Element { value: self.#ident })?;
594 }
595 } else {
596 quote! {
597 {
598 let __s = ::std::string::ToString::to_string(&self.#ident);
599 emitter.emit(::edifact_rs::EdifactEvent::Element { value: &__s })?;
600 }
601 }
602 }
603}
604
605fn emit_component_element(ident: &syn::Ident, ty: &Type) -> TokenStream2 {
609 if is_option_type(ty) {
610 let inner_is_str = option_inner_type(ty).is_some_and(is_str_like);
611 if inner_is_str {
612 quote! {
613 match &self.#ident {
614 ::core::option::Option::Some(__v) => {
615 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: __v.as_str() })?;
616 }
617 ::core::option::Option::None => {
618 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: "" })?;
619 }
620 }
621 }
622 } else {
623 quote! {
624 match &self.#ident {
625 ::core::option::Option::Some(__v) => {
626 let __s = ::std::string::ToString::to_string(__v);
627 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: &__s })?;
628 }
629 ::core::option::Option::None => {
630 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: "" })?;
631 }
632 }
633 }
634 }
635 } else if is_string_type(ty) {
636 quote! {
637 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: self.#ident.as_str() })?;
638 }
639 } else if is_str_ref_type(ty) {
640 quote! {
641 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: self.#ident })?;
642 }
643 } else {
644 quote! {
645 {
646 let __s = ::std::string::ToString::to_string(&self.#ident);
647 emitter.emit(::edifact_rs::EdifactEvent::ComponentElement { value: &__s })?;
648 }
649 }
650 }
651}
652
653fn emit_composite_field(ident: &syn::Ident, ty: &Type) -> TokenStream2 {
655 if is_option_type(ty) {
656 quote! {
657 match &self.#ident {
658 ::core::option::Option::Some(__v) => {
659 ::edifact_rs::EdifactCompositeSerialize::edifact_serialize_composite(__v, emitter)?;
660 }
661 ::core::option::Option::None => {
662 emitter.emit(::edifact_rs::EdifactEvent::Element { value: "" })?;
663 }
664 }
665 }
666 } else {
667 quote! {
668 ::edifact_rs::EdifactCompositeSerialize::edifact_serialize_composite(&self.#ident, emitter)?;
669 }
670 }
671}
672
673fn impl_deserialize(input: &DeriveInput) -> syn::Result<TokenStream2> {
676 let name = &input.ident;
677 let struct_attrs = parse_struct_attrs(input)?;
678 let fields = get_named_fields(input)?;
679 let is_segment_struct = struct_attrs.segment.is_some();
680
681 let field_data: Vec<(&syn::Ident, &Type, FieldAttrs)> = fields
682 .named
683 .iter()
684 .map(|f| {
685 let attrs = parse_field_attrs(f)?;
686 let ident = f
687 .ident
688 .as_ref()
689 .ok_or_else(|| syn::Error::new_spanned(f, "only named fields are supported"))?;
690 validate_field_attrs(ident, &f.ty, &attrs, is_segment_struct)?;
691 Ok((ident, &f.ty, attrs))
692 })
693 .collect::<syn::Result<_>>()?;
694
695 let field_names: Vec<&syn::Ident> = field_data.iter().map(|(id, _, _)| *id).collect();
696
697 let (body, owned_body, segment_tag_impl) = if let Some(seg_tag) = &struct_attrs.segment {
698 let qualifier_guard = if let Some(qual) = &struct_attrs.qualifier {
700 quote! {
701 if __seg.element_str(0).unwrap_or("") != #qual {
702 return ::core::result::Result::Err(
703 ::edifact_rs::EdifactError::MissingRequiredElement {
704 tag: #seg_tag.to_owned(),
705 element_index: 0,
706 }
707 );
708 }
709 }
710 } else if let Some(idx) = struct_attrs.qualifier_from {
711 quote! {
712 match __seg.element_str(#idx as usize) {
713 None => return ::core::result::Result::Err(
714 ::edifact_rs::EdifactError::MissingRequiredElement {
715 tag: #seg_tag.to_owned(),
716 element_index: #idx as usize,
717 }
718 ),
719 Some("") => return ::core::result::Result::Err(
720 ::edifact_rs::EdifactError::InvalidFieldValue {
721 tag: #seg_tag.to_owned(),
722 element_index: #idx as usize,
723 value: ::std::string::String::new(),
724 }
725 ),
726 Some(__qual_val) => { let _ = __qual_val; }
727 }
728 }
729 } else {
730 quote! {}
731 };
732
733 let find_seg = if let Some(qual) = &struct_attrs.qualifier {
734 quote! {
735 ::edifact_rs::__private::find_qualified_segment(segments, #seg_tag, #qual)
736 }
737 } else {
738 quote! {
739 ::edifact_rs::__private::find_segment(segments, #seg_tag)
740 }
741 };
742
743 let field_inits: Vec<TokenStream2> = field_data
744 .iter()
745 .enumerate()
746 .map(|(decl_i, (ident, ty, attrs))| -> syn::Result<TokenStream2> {
747 let idx = attrs.element.unwrap_or(decl_i as u32) as usize;
748 if attrs.composite {
749 if is_option_type(ty) {
750 let inner_ty = option_inner_type(ty)
751 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
752 return Ok(quote! {
753 let #ident = match ::edifact_rs::__private::composite_element(__seg, #idx) {
754 ::core::option::Option::Some(__composite) => {
755 ::core::option::Option::Some(
756 <#inner_ty as ::edifact_rs::EdifactCompositeDeserialize>::edifact_deserialize_composite(__composite)?
757 )
758 }
759 ::core::option::Option::None => ::core::option::Option::None,
760 };
761 });
762 }
763 return Ok(quote! {
764 let #ident = <#ty as ::edifact_rs::EdifactCompositeDeserialize>::edifact_deserialize_composite(
765 ::edifact_rs::__private::composite_element(__seg, #idx).ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
766 tag: #seg_tag.to_owned(),
767 element_index: #idx as usize,
768 })?
769 )?;
770 });
771 }
772 let value_expr = if let Some(comp) = attrs.component {
773 let comp = comp as usize;
774 quote! {
775 __seg.get_element(#idx).and_then(|__e| __e.get_component(#comp))
776 }
777 } else {
778 quote! { __seg.element_str(#idx) }
779 };
780 Ok(if is_option_type(ty) {
781 let inner_ty = option_inner_type(ty);
782 let inner_is_str = inner_ty.is_some_and(is_str_like);
783 if inner_is_str {
784 quote! {
785 let #ident = #value_expr
786 .filter(|__s| !__s.is_empty())
787 .map(::std::string::String::from);
788 }
789 } else {
790 let inner_ty = inner_ty
791 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
792 quote! {
793 let #ident = #value_expr
794 .filter(|__s| !__s.is_empty())
795 .map(|__s| __s.parse::<#inner_ty>()
796 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })
797 )
798 .transpose()?;
799 }
800 }
801 } else if is_str_like(ty) {
802 quote! {
803 let #ident = #value_expr
804 .filter(|__s| !__s.is_empty())
805 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
806 tag: #seg_tag.to_owned(),
807 element_index: #idx as usize,
808 })?
809 .to_owned();
810 }
811 } else {
812 quote! {
813 let #ident = #value_expr
814 .filter(|__s| !__s.is_empty())
815 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
816 tag: #seg_tag.to_owned(),
817 element_index: #idx as usize,
818 })?
819 .parse::<#ty>()
820 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })?;
821 }
822 })
823 })
824 .collect::<syn::Result<_>>()?;
825
826 let body = quote! {
827 let __seg = #find_seg
828 .ok_or_else(|| ::edifact_rs::EdifactError::MissingSegment {
829 tag: #seg_tag.to_owned(),
830 expected_position: "message body".to_owned(),
831 })?;
832 #qualifier_guard
833 #(#field_inits)*
834 ::core::result::Result::Ok(Self { #(#field_names),* })
835 };
836
837 let qualifier_match = if let Some(qual) = &struct_attrs.qualifier {
839 quote! {
840 fn matches_segment(seg: &::edifact_rs::Segment<'_>) -> bool {
841 seg.tag == Self::SEGMENT_TAG
842 && seg.element_str(0).unwrap_or("") == #qual
843 }
844 }
845 } else if let Some(idx) = struct_attrs.qualifier_from {
846 quote! {
847 fn matches_segment(seg: &::edifact_rs::Segment<'_>) -> bool {
848 seg.tag == Self::SEGMENT_TAG
849 && !seg.element_str(#idx as usize).unwrap_or("").is_empty()
850 }
851 }
852 } else {
853 quote! {}
854 };
855
856 let seg_tag_impl = quote! {
857 impl ::edifact_rs::EdifactSegmentTag for #name {
858 const SEGMENT_TAG: &'static str = #seg_tag;
859 #qualifier_match
860 }
861 };
862
863 let find_seg_owned = if let Some(qual) = &struct_attrs.qualifier {
866 quote! {
867 ::edifact_rs::__private::find_qualified_segment_owned(segments, #seg_tag, #qual)
868 }
869 } else {
870 quote! {
871 ::edifact_rs::__private::find_segment_owned(segments, #seg_tag)
872 }
873 };
874
875 let field_inits_owned: Vec<TokenStream2> = field_data
876 .iter()
877 .enumerate()
878 .map(|(decl_i, (ident, ty, attrs))| -> syn::Result<TokenStream2> {
879 let idx = attrs.element.unwrap_or(decl_i as u32) as usize;
880 if attrs.composite {
881 if is_option_type(ty) {
882 let inner_ty = option_inner_type(ty)
883 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
884 return Ok(quote! {
885 let #ident = match __seg.elements.get(#idx) {
886 ::core::option::Option::Some(__e) => {
887 let __cows = __e.components.iter()
888 .map(|s| ::std::borrow::Cow::Borrowed(s.as_str()))
889 .collect::<::std::vec::Vec<::std::borrow::Cow<'_, str>>>();
890 ::core::option::Option::Some(
891 <#inner_ty as ::edifact_rs::EdifactCompositeDeserialize>::edifact_deserialize_composite(
892 ::edifact_rs::CompositeElement::from_slice(&__cows)
893 )?
894 )
895 }
896 ::core::option::Option::None => ::core::option::Option::None,
897 };
898 });
899 }
900 return Ok(quote! {
901 let #ident = {
902 let __cows = __seg.elements.get(#idx)
903 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
904 tag: #seg_tag.to_owned(),
905 element_index: #idx as usize,
906 })?
907 .components.iter()
908 .map(|s| ::std::borrow::Cow::Borrowed(s.as_str()))
909 .collect::<::std::vec::Vec<::std::borrow::Cow<'_, str>>>();
910 <#ty as ::edifact_rs::EdifactCompositeDeserialize>::edifact_deserialize_composite(
911 ::edifact_rs::CompositeElement::from_slice(&__cows)
912 )?
913 };
914 });
915 }
916 let value_expr_owned = if let Some(comp) = attrs.component {
917 let comp = comp as usize;
918 quote! { __seg.component_str(#idx, #comp) }
919 } else {
920 quote! { __seg.element_str(#idx) }
921 };
922 Ok(if is_option_type(ty) {
923 if let Some(inner_ty) = option_inner_type(ty) {
924 if is_str_like(inner_ty) {
925 quote! {
926 let #ident = #value_expr_owned
927 .filter(|__s| !__s.is_empty())
928 .map(::std::string::String::from);
929 }
930 } else {
931 quote! {
932 let #ident = #value_expr_owned
933 .filter(|__s| !__s.is_empty())
934 .map(|__s| __s.parse::<#inner_ty>()
935 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })
936 )
937 .transpose()?;
938 }
939 }
940 } else {
941 quote! {
943 let #ident = #value_expr_owned
944 .filter(|__s| !__s.is_empty())
945 .map(::std::string::String::from);
946 }
947 }
948 } else if is_str_like(ty) {
949 quote! {
950 let #ident = #value_expr_owned
951 .filter(|__s| !__s.is_empty())
952 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
953 tag: #seg_tag.to_owned(),
954 element_index: #idx as usize,
955 })?
956 .to_owned();
957 }
958 } else {
959 quote! {
960 let #ident = #value_expr_owned
961 .filter(|__s| !__s.is_empty())
962 .ok_or_else(|| ::edifact_rs::EdifactError::MissingRequiredElement {
963 tag: #seg_tag.to_owned(),
964 element_index: #idx as usize,
965 })?
966 .parse::<#ty>()
967 .map_err(|_| ::edifact_rs::EdifactError::InvalidText { offset: __seg.span.start })?;
968 }
969 })
970 })
971 .collect::<syn::Result<_>>()?;
972
973 let owned_body = quote! {
974 let __seg = #find_seg_owned
975 .ok_or_else(|| ::edifact_rs::EdifactError::MissingSegment {
976 tag: #seg_tag.to_owned(),
977 expected_position: "message body".to_owned(),
978 })?;
979 #qualifier_guard
980 #(#field_inits_owned)*
981 ::core::result::Result::Ok(Self { #(#field_names),* })
982 };
983
984 (body, owned_body, seg_tag_impl)
985 } else {
986 let field_inits: Vec<TokenStream2> = field_data
988 .iter()
989 .map(|(ident, ty, attrs)| -> syn::Result<TokenStream2> {
990 Ok(if let Some(qual) = &attrs.qualifier {
991 if attrs.group || is_vec_type(ty) {
992 let inner_ty = vec_inner_type(ty)
993 .ok_or_else(|| syn::Error::new(ident.span(), "expected Vec<T>"))?;
994 quote! {
995 let #ident = segments
996 .iter()
997 .filter(|__seg| {
998 __seg.tag == <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG
999 && __seg.element_str(0).unwrap_or("") == #qual
1000 })
1001 .map(|__seg| {
1002 ::edifact_rs::EdifactDeserialize::edifact_deserialize(
1003 ::core::slice::from_ref(__seg),
1004 )
1005 })
1006 .collect::<::core::result::Result<::std::vec::Vec<#inner_ty>, ::edifact_rs::EdifactError>>()?;
1007 }
1008 } else if is_option_type(ty) {
1009 let inner_ty = option_inner_type(ty)
1010 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1011 quote! {
1012 let #ident = match ::edifact_rs::__private::find_qualified_segment(
1013 segments,
1014 <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG,
1015 #qual,
1016 ) {
1017 ::core::option::Option::Some(__seg) => {
1018 ::core::option::Option::Some(
1019 ::edifact_rs::EdifactDeserialize::edifact_deserialize(
1020 ::core::slice::from_ref(__seg),
1021 )?
1022 )
1023 }
1024 ::core::option::Option::None => ::core::option::Option::None,
1025 };
1026 }
1027 } else {
1028 quote! {
1029 let __seg = ::edifact_rs::__private::find_qualified_segment(
1030 segments,
1031 <#ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG,
1032 #qual,
1033 )
1034 .ok_or_else(|| ::edifact_rs::EdifactError::MissingSegment {
1035 tag: <#ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG.to_owned(),
1036 expected_position: "message body".to_owned(),
1037 })?;
1038 let #ident = ::edifact_rs::EdifactDeserialize::edifact_deserialize(
1039 ::core::slice::from_ref(__seg),
1040 )?;
1041 }
1042 }
1043 } else if attrs.group || is_vec_type(ty) {
1044 let inner_ty = vec_inner_type(ty)
1045 .ok_or_else(|| syn::Error::new(ident.span(), "expected Vec<T>"))?;
1046 quote! {
1047 let #ident = ::edifact_rs::__private::find_segments_typed::<#inner_ty>(segments)
1048 .map(|__seg| {
1049 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize(
1050 ::core::slice::from_ref(__seg),
1051 )
1052 })
1053 .collect::<::core::result::Result<::std::vec::Vec<#inner_ty>, _>>()?;
1054 }
1055 } else if is_option_type(ty) {
1056 let inner_ty = option_inner_type(ty)
1057 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1058 quote! {
1059 let #ident = if segments
1060 .iter()
1061 .any(|__seg| __seg.tag == <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG)
1062 {
1063 ::core::option::Option::Some(
1064 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize(segments)?
1065 )
1066 } else {
1067 ::core::option::Option::None
1068 };
1069 }
1070 } else {
1071 quote! {
1072 let #ident = ::edifact_rs::EdifactDeserialize::edifact_deserialize(segments)?;
1073 }
1074 })
1075 })
1076 .collect::<syn::Result<_>>()?;
1077
1078 let body = quote! {
1079 #(#field_inits)*
1080 ::core::result::Result::Ok(Self { #(#field_names),* })
1081 };
1082
1083 let field_inits_owned: Vec<TokenStream2> = field_data
1086 .iter()
1087 .map(|(ident, ty, attrs)| -> syn::Result<TokenStream2> {
1088 Ok(if let Some(qual) = &attrs.qualifier {
1089 if attrs.group || is_vec_type(ty) {
1090 let inner_ty = vec_inner_type(ty)
1091 .ok_or_else(|| syn::Error::new(ident.span(), "expected Vec<T>"))?;
1092 quote! {
1093 let #ident = segments
1094 .iter()
1095 .filter(|__seg| {
1096 __seg.tag == <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG
1097 && __seg.element_str(0).unwrap_or("") == #qual
1098 })
1099 .map(|__seg| {
1100 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(
1101 ::core::slice::from_ref(__seg),
1102 )
1103 })
1104 .collect::<::core::result::Result<::std::vec::Vec<#inner_ty>, ::edifact_rs::EdifactError>>()?;
1105 }
1106 } else if is_option_type(ty) {
1107 let inner_ty = option_inner_type(ty)
1108 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1109 quote! {
1110 let #ident = match ::edifact_rs::__private::find_qualified_segment_owned(
1111 segments,
1112 <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG,
1113 #qual,
1114 ) {
1115 ::core::option::Option::Some(__seg) => {
1116 ::core::option::Option::Some(
1117 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(
1118 ::core::slice::from_ref(__seg),
1119 )?
1120 )
1121 }
1122 ::core::option::Option::None => ::core::option::Option::None,
1123 };
1124 }
1125 } else {
1126 quote! {
1127 let __seg = ::edifact_rs::__private::find_qualified_segment_owned(
1128 segments,
1129 <#ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG,
1130 #qual,
1131 )
1132 .ok_or_else(|| ::edifact_rs::EdifactError::MissingSegment {
1133 tag: <#ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG.to_owned(),
1134 expected_position: "message body".to_owned(),
1135 })?;
1136 let #ident = <#ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(
1137 ::core::slice::from_ref(__seg),
1138 )?;
1139 }
1140 }
1141 } else if attrs.group || is_vec_type(ty) {
1142 let inner_ty = vec_inner_type(ty)
1143 .ok_or_else(|| syn::Error::new(ident.span(), "expected Vec<T>"))?;
1144 quote! {
1145 let #ident = segments
1146 .iter()
1147 .filter(|__seg| <#inner_ty as ::edifact_rs::EdifactSegmentTag>::matches_owned_segment(__seg))
1148 .map(|__seg| {
1149 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(
1150 ::core::slice::from_ref(__seg),
1151 )
1152 })
1153 .collect::<::core::result::Result<::std::vec::Vec<#inner_ty>, _>>()?;
1154 }
1155 } else if is_option_type(ty) {
1156 let inner_ty = option_inner_type(ty)
1157 .ok_or_else(|| syn::Error::new(ident.span(), "expected Option<T>"))?;
1158 quote! {
1159 let #ident = if segments
1160 .iter()
1161 .any(|__seg| __seg.tag == <#inner_ty as ::edifact_rs::EdifactSegmentTag>::SEGMENT_TAG)
1162 {
1163 ::core::option::Option::Some(
1164 <#inner_ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(segments)?
1165 )
1166 } else {
1167 ::core::option::Option::None
1168 };
1169 }
1170 } else {
1171 quote! {
1172 let #ident = <#ty as ::edifact_rs::EdifactDeserialize>::edifact_deserialize_owned(segments)?;
1173 }
1174 })
1175 })
1176 .collect::<syn::Result<_>>()?;
1177
1178 let owned_body = quote! {
1179 #(#field_inits_owned)*
1180 ::core::result::Result::Ok(Self { #(#field_names),* })
1181 };
1182
1183 (body, owned_body, quote! {})
1184 };
1185
1186 Ok(quote! {
1187 impl ::edifact_rs::EdifactDeserialize for #name {
1188 fn edifact_deserialize(
1189 segments: &[::edifact_rs::Segment<'_>],
1190 ) -> ::core::result::Result<Self, ::edifact_rs::EdifactError> {
1191 #body
1192 }
1193
1194 fn edifact_deserialize_owned(
1195 segments: &[::edifact_rs::OwnedSegment],
1196 ) -> ::core::result::Result<Self, ::edifact_rs::EdifactError> {
1197 #owned_body
1198 }
1199 }
1200 #segment_tag_impl
1201 })
1202}
1203
1204#[cfg(test)]
1205mod tests {
1206 #[test]
1207 fn trybuild_ui() {
1208 let t = trybuild::TestCases::new();
1209 t.pass("tests/ui/pass_*.rs");
1210 t.compile_fail("tests/ui/fail_*.rs");
1211 }
1212}