xso_proc/field/
text.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
// Copyright (c) 2024 Jonas Schäfer <jonas@zombofant.net>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//! This module concerns the processing of text content.
//!
//! In particular, it provides the `#[xml(text)]` implementation.

use proc_macro2::Span;
use quote::quote;
use syn::*;

use crate::error_message::ParentRef;
use crate::scope::{AsItemsScope, FromEventsScope};
use crate::types::{
    as_xml_text_fn, from_xml_text_fn, string_ty, text_codec_decode_fn, text_codec_encode_fn,
};

use super::{Field, FieldBuilderPart, FieldIteratorPart, FieldTempInit};

/// The field maps to the character data of the element.
pub(super) struct TextField {
    /// Optional codec to use
    pub(super) codec: Option<Expr>,
}

impl Field for TextField {
    fn make_builder_part(
        &self,
        scope: &FromEventsScope,
        _container_name: &ParentRef,
        member: &Member,
        ty: &Type,
    ) -> Result<FieldBuilderPart> {
        let FromEventsScope { ref text, .. } = scope;
        let field_access = scope.access_field(member);
        let finalize = match self.codec {
            Some(ref codec) => {
                let decode = text_codec_decode_fn(ty.clone());
                quote! {
                    #decode(&#codec, #field_access)?
                }
            }
            None => {
                let from_xml_text = from_xml_text_fn(ty.clone());
                quote! { #from_xml_text(#field_access)? }
            }
        };

        Ok(FieldBuilderPart::Text {
            value: FieldTempInit {
                init: quote! { ::xso::exports::alloc::string::String::new() },
                ty: string_ty(Span::call_site()),
            },
            collect: quote! {
                #field_access.push_str(#text.as_str());
            },
            finalize,
        })
    }

    fn make_iterator_part(
        &self,
        _scope: &AsItemsScope,
        _container_name: &ParentRef,
        bound_name: &Ident,
        _member: &Member,
        ty: &Type,
    ) -> Result<FieldIteratorPart> {
        let generator = match self.codec {
            Some(ref codec) => {
                let encode = text_codec_encode_fn(ty.clone());
                quote! { #encode(&#codec, #bound_name)? }
            }
            None => {
                let as_xml_text = as_xml_text_fn(ty.clone());
                quote! { ::core::option::Option::Some(#as_xml_text(#bound_name)?) }
            }
        };

        Ok(FieldIteratorPart::Text { generator })
    }

    fn captures_text(&self) -> bool {
        true
    }
}