xso_proc/field/
attribute.rs1use quote::{quote, ToTokens};
12use syn::*;
13
14use crate::error_message::{self, ParentRef};
15use crate::meta::{Flag, NameRef, NamespaceRef};
16use crate::scope::{AsItemsScope, FromEventsScope};
17use crate::types::{
18 as_optional_xml_text_fn, default_fn, from_xml_text_fn, text_codec_decode_fn,
19 text_codec_encode_fn,
20};
21
22use super::{Field, FieldBuilderPart, FieldIteratorPart, FieldTempInit};
23
24pub(super) struct AttributeField {
26 pub(super) xml_namespace: Option<NamespaceRef>,
28
29 pub(super) xml_name: NameRef,
31
32 pub(super) default_: Flag,
35
36 pub(super) codec: Option<Expr>,
38}
39
40impl Field for AttributeField {
41 fn make_builder_part(
42 &self,
43 scope: &FromEventsScope,
44 container_name: &ParentRef,
45 member: &Member,
46 ty: &Type,
47 ) -> Result<FieldBuilderPart> {
48 let FromEventsScope { ref attrs, .. } = scope;
49 let ty = ty.clone();
50 let xml_namespace = &self.xml_namespace;
51 let xml_name = &self.xml_name;
52
53 let missing_msg = error_message::on_missing_attribute(container_name, member);
54
55 let xml_namespace = match xml_namespace {
56 Some(v) => v.to_token_stream(),
57 None => quote! {
58 ::xso::exports::rxml::Namespace::none()
59 },
60 };
61
62 let finalize = match self.codec {
63 Some(ref codec) => {
64 let decode = text_codec_decode_fn(ty.clone());
65 quote! {
66 |value| #decode(&#codec, value)
67 }
68 }
69 None => {
70 let from_xml_text = from_xml_text_fn(ty.clone());
71 quote! { #from_xml_text }
72 }
73 };
74
75 let on_absent = match self.default_ {
76 Flag::Absent => quote! {
77 return ::core::result::Result::Err(::xso::error::Error::Other(#missing_msg).into())
78 },
79 Flag::Present(_) => {
80 let default_ = default_fn(ty.clone());
81 quote! {
82 #default_()
83 }
84 }
85 };
86
87 Ok(FieldBuilderPart::Init {
88 value: FieldTempInit {
89 init: quote! {
90 match #attrs.remove(#xml_namespace, #xml_name).map(#finalize).transpose()? {
91 ::core::option::Option::Some(v) => v,
92 ::core::option::Option::None => #on_absent,
93 }
94 },
95 ty: ty.clone(),
96 },
97 })
98 }
99
100 fn make_iterator_part(
101 &self,
102 _scope: &AsItemsScope,
103 _container_name: &ParentRef,
104 bound_name: &Ident,
105 _member: &Member,
106 ty: &Type,
107 ) -> Result<FieldIteratorPart> {
108 let xml_namespace = match self.xml_namespace {
109 Some(ref v) => quote! { ::xso::exports::rxml::Namespace::from(#v) },
110 None => quote! {
111 ::xso::exports::rxml::Namespace::NONE
112 },
113 };
114 let xml_name = &self.xml_name;
115
116 let generator = match self.codec {
117 Some(ref codec) => {
118 let encode = text_codec_encode_fn(ty.clone());
119 quote! { #encode(&#codec, #bound_name)? }
120 }
121 None => {
122 let as_optional_xml_text = as_optional_xml_text_fn(ty.clone());
123 quote! { #as_optional_xml_text(#bound_name)? }
124 }
125 };
126
127 Ok(FieldIteratorPart::Header {
128 generator: quote! {
129 #generator.map(|#bound_name| ::xso::Item::Attribute(
130 #xml_namespace,
131 ::xso::exports::alloc::borrow::Cow::Borrowed(#xml_name),
132 #bound_name,
133 ));
134 },
135 })
136 }
137}