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 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
//! Infrastructure for parsing fields from text.
use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned};
use syn::{spanned::Spanned, *};
use crate::error_message::ParentRef;
use super::{Field, FieldParsePart};
/// A field parsed from a XML text.
///
/// Maps to `#[xml(text)]`.
#[derive(Debug)]
pub(crate) struct TextField {
/// The codec implementation to use.
///
/// If set, parsing does not use the `FromXmlText` / `IntoXmlText` traits
/// but instead uses the `TextCodec` trait on the given type.
pub(super) codec: Option<Type>,
}
impl TextField {
/// Construct a new `#[xml(text)]` field.
///
/// `codec` must be the value of the `codec = ..` option, which, if given
/// overrides how the text is converted to a rust value.
///
/// `attr_span` is used for emitting error messages when no better span
/// can be constructed. This should point at the `#[xml(..)]` meta of the
/// field or another closely-related object.
pub(super) fn new(_attr_span: &Span, codec: Option<Type>) -> Result<Self> {
Ok(Self { codec })
}
}
impl Field for TextField {
fn is_text(&self) -> bool {
true
}
fn build_try_from_element(
&self,
_container_name: &ParentRef,
_container_namespace_expr: &Expr,
tempname: Ident,
_member: &Member,
ty: &Type,
) -> Result<FieldParsePart> {
let decode = match self.codec {
Some(ref codec_ty) => {
let codec_ty_decode = quote_spanned! {codec_ty.span()=> <#codec_ty as ::xso::TextCodec::<#ty>>::decode};
quote! {
#codec_ty_decode(&residual.text())?
}
}
None => {
let ty_from_xml_text =
quote_spanned! {ty.span()=> <#ty as ::xso::FromXmlText>::from_xml_text};
quote! {
#ty_from_xml_text(&residual.text())?
}
}
};
Ok(FieldParsePart {
tempinit: quote! {
let #tempname: #ty = #decode;
},
value: quote! { #tempname },
..FieldParsePart::default()
})
}
fn build_into_element(
&self,
_container_name: &ParentRef,
_container_namespace_expr: &Expr,
_member: &Member,
ty: &Type,
access: Expr,
) -> Result<TokenStream> {
let encode = match self.codec {
Some(ref codec_ty) => {
let codec_ty_encode = quote_spanned! {codec_ty.span()=> <#codec_ty as ::xso::TextCodec::<#ty>>::encode};
quote! {
#codec_ty_encode(#access).unwrap_or_else(String::new)
}
}
None => {
let ty_into_xml_text =
quote_spanned! {ty.span()=> <#ty as ::xso::IntoXmlText>::into_xml_text};
quote! {
#ty_into_xml_text(#access)
}
}
};
Ok(quote! {
{
let __text = #encode;
if __text.len() > 0 {
builder.append(::xso::exports::minidom::Node::Text(__text))
} else {
builder
}
}
})
}
fn build_set_namespace(
&self,
_input: &Ident,
_ty: &Type,
_access: Expr,
) -> Result<TokenStream> {
Ok(TokenStream::default())
}
}