mirror of
https://gitlab.com/xmpp-rs/xmpp-rs.git
synced 2024-06-17 21:35:25 +02:00
136 lines
4.0 KiB
Rust
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())
|
|
}
|
|
}
|