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