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