1
0
mirror of https://gitlab.com/xmpp-rs/xmpp-rs.git synced 2024-06-17 21:35:25 +02:00
xmpp-rs/parsers-macros/src/field/flag.rs
Jonas Schäfer decbeb351f parsers-macros: introduce Field trait and object
This simplifies the code of FieldDef and makes it more extensible for
future ideas.
2024-04-05 15:53:48 +02:00

136 lines
4.0 KiB
Rust

//! Infrastructure for parsing boolean fields indicating child presence.
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::*;
use crate::error_message::{self, ParentRef};
use crate::meta::{Name, NameRef, NamespaceRef, StaticNamespace};
use super::{Field, FieldParsePart};
/// A field parsed from the presence of an empty XML child.
///
/// Maps to `#[xml(flag)]`.
#[derive(Debug)]
pub(crate) struct FlagField {
/// The XML namespace of the child element to look for.
namespace: StaticNamespace,
/// The XML name of the child element to look for.
name: Name,
}
impl FlagField {
/// Construct a new `#[xml(flag)]` field.
///
/// `namespace` and `name` must both be set and `namespace` must be a
/// [`NamespaceRef::Static`].
///
/// `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,
namespace: Option<NamespaceRef>,
name: Option<NameRef>,
) -> Result<Self> {
let namespace = match namespace {
None => {
return Err(Error::new(
attr_span.clone(),
"#[xml(flag)] requires namespace attribute",
))
}
Some(NamespaceRef::Static(ns)) => ns,
Some(NamespaceRef::Dyn(ns)) => {
return Err(Error::new_spanned(
ns,
"dynamic namespaces cannot be used with #[xml(flag)]",
))
}
Some(NamespaceRef::Super(ns)) => {
return Err(Error::new_spanned(
ns,
"flag elements cannot refer to the parent namespace",
))
}
};
let name = match name {
None => {
return Err(Error::new(
attr_span.clone(),
"#[xml(flag)] requires name attribute",
))
}
Some(name) => name,
};
Ok(Self {
namespace,
name: name.into(),
})
}
}
impl Field for FlagField {
fn build_try_from_element(
&self,
container_name: &ParentRef,
_container_namespace_expr: &Expr,
tempname: Ident,
member: &Member,
_ty: &Type,
) -> Result<FieldParsePart> {
let field_name = &self.name;
let field_namespace = &self.namespace;
let duperr = error_message::on_duplicate_child(container_name, member);
Ok(FieldParsePart {
tempinit: quote! {
let mut #tempname = false;
},
childiter: quote! {
residual = if residual.is(#field_name, #field_namespace) {
// TODO: reject contents
if #tempname {
return Err(::xmpp_parsers_core::error::Error::ParseError(#duperr));
}
#tempname = true;
continue;
} else {
residual
};
},
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 child_name = &self.name;
let child_namespace = &self.namespace;
Ok(quote! {
if #access {
builder.append(::xmpp_parsers_core::exports::minidom::Node::Element(::xmpp_parsers_core::exports::minidom::Element::builder(#child_name, #child_namespace).build()))
} else {
builder
}
})
}
fn build_set_namespace(
&self,
_input: &Ident,
_ty: &Type,
_access: Expr,
) -> Result<TokenStream> {
Ok(TokenStream::default())
}
}