xso_proc/field/
text.rs

1// Copyright (c) 2024 Jonas Schäfer <jonas@zombofant.net>
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7//! This module concerns the processing of text content.
8//!
9//! In particular, it provides the `#[xml(text)]` implementation.
10
11use proc_macro2::Span;
12use quote::quote;
13use syn::*;
14
15use crate::error_message::ParentRef;
16use crate::scope::{AsItemsScope, FromEventsScope};
17use crate::types::{
18    as_xml_text_fn, from_xml_text_fn, string_ty, text_codec_decode_fn, text_codec_encode_fn,
19};
20
21use super::{Field, FieldBuilderPart, FieldIteratorPart, FieldTempInit};
22
23/// The field maps to the character data of the element.
24pub(super) struct TextField {
25    /// Optional codec to use
26    pub(super) codec: Option<Expr>,
27}
28
29impl Field for TextField {
30    fn make_builder_part(
31        &self,
32        scope: &FromEventsScope,
33        _container_name: &ParentRef,
34        member: &Member,
35        ty: &Type,
36    ) -> Result<FieldBuilderPart> {
37        let FromEventsScope { ref text, .. } = scope;
38        let field_access = scope.access_field(member);
39        let finalize = match self.codec {
40            Some(ref codec) => {
41                let decode = text_codec_decode_fn(ty.clone());
42                quote! {
43                    #decode(&#codec, #field_access)?
44                }
45            }
46            None => {
47                let from_xml_text = from_xml_text_fn(ty.clone());
48                quote! { #from_xml_text(#field_access)? }
49            }
50        };
51
52        Ok(FieldBuilderPart::Text {
53            value: FieldTempInit {
54                init: quote! { ::xso::exports::alloc::string::String::new() },
55                ty: string_ty(Span::call_site()),
56            },
57            collect: quote! {
58                #field_access.push_str(#text.as_str());
59            },
60            finalize,
61        })
62    }
63
64    fn make_iterator_part(
65        &self,
66        _scope: &AsItemsScope,
67        _container_name: &ParentRef,
68        bound_name: &Ident,
69        _member: &Member,
70        ty: &Type,
71    ) -> Result<FieldIteratorPart> {
72        let generator = match self.codec {
73            Some(ref codec) => {
74                let encode = text_codec_encode_fn(ty.clone());
75                quote! { #encode(&#codec, #bound_name)? }
76            }
77            None => {
78                let as_xml_text = as_xml_text_fn(ty.clone());
79                quote! { ::core::option::Option::Some(#as_xml_text(#bound_name)?) }
80            }
81        };
82
83        Ok(FieldIteratorPart::Text { generator })
84    }
85
86    fn captures_text(&self) -> bool {
87        true
88    }
89}