Implement #[xml(flag)] meta

This commit is contained in:
Jonas Schäfer 2025-01-20 17:51:22 +01:00
commit 727e57b756
10 changed files with 423 additions and 2 deletions

View file

@ -27,7 +27,8 @@ assert_eq!(foo, Foo);
2. [`child` meta](#child-meta)
3. [`element` meta](#element-meta)
4. [`extract` meta](#extract-meta)
5. [`text` meta](#text-meta)
5. [`flag` meta](#flag-meta)
6. [`text` meta](#text-meta)
## Attributes
@ -557,6 +558,47 @@ assert_eq!(foo, Foo {
});
```
### `flag` meta
The `flag` meta causes the field to be mapped to a single, optional child element. Absence of the child is equivalent to the value `false`, presence
of the child element is equivalent to the value `true`.
The following keys can be used inside the `#[xml(flag(..))]` meta:
| Key | Value type | Description |
| --- | --- | --- |
| `namespace` | *string literal* or *path* | The optional namespace of the XML attribute to match. If it is a *path*, it must point at a `&'static str`. |
| `name` | *string literal* or *path* | The name of the XML attribute to match. If it is a *path*, it must point at a `&'static NcNameStr`. |
The field on which the `flag` meta is used must be of type `bool`.
If `namespace` is not set, it defaults to the namespace of the surrounding
container. If `name` is not set, it defaults to the field's name, if available.
If `name` is not set and the field is unnamed, a compile-time error is raised.
When parsing, any contents within the child element generate a parse error.
#### Example
```rust
# use xso::FromXml;
#[derive(FromXml, Debug, PartialEq)]
#[xml(namespace = "urn:example", name = "foo")]
struct Foo {
#[xml(flag(name = "flag"))]
flag: bool,
};
let foo: Foo = xso::from_bytes(b"<foo xmlns='urn:example'><flag/></foo>").unwrap();
assert_eq!(foo, Foo {
flag: true,
});
let foo: Foo = xso::from_bytes(b"<foo xmlns='urn:example'/>").unwrap();
assert_eq!(foo, Foo {
flag: false,
});
```
### `text` meta
The `text` meta causes the field to be mapped to the text content of the

View file

@ -269,6 +269,58 @@ impl FromEventsBuilder for Discard {
}
}
/// Builder which discards the contents (or raises on unexpected contents).
///
/// This builder is only to be used from within the proc macros and is not
/// stable, public API.
#[doc(hidden)]
#[cfg(feature = "macros")]
pub struct EmptyBuilder {
childerr: &'static str,
texterr: &'static str,
}
#[cfg(feature = "macros")]
impl FromEventsBuilder for EmptyBuilder {
type Output = ();
fn feed(&mut self, ev: rxml::Event) -> Result<Option<Self::Output>, Error> {
match ev {
rxml::Event::EndElement(..) => Ok(Some(())),
rxml::Event::StartElement(..) => Err(Error::Other(self.childerr)),
rxml::Event::Text(..) => Err(Error::Other(self.texterr)),
_ => Err(Error::Other(
"unexpected content in supposed-to-be-empty element",
)),
}
}
}
/// Precursor struct for [`EmptyBuilder`].
///
/// This struct is only to be used from within the proc macros and is not
/// stable, public API.
#[doc(hidden)]
#[cfg(feature = "macros")]
pub struct Empty {
pub attributeerr: &'static str,
pub childerr: &'static str,
pub texterr: &'static str,
}
#[cfg(feature = "macros")]
impl Empty {
pub fn start(self, attr: rxml::AttrMap) -> Result<EmptyBuilder, Error> {
if attr.len() > 0 {
return Err(Error::Other(self.attributeerr));
}
Ok(EmptyBuilder {
childerr: self.childerr,
texterr: self.texterr,
})
}
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -39,10 +39,23 @@ mod rxml_util;
pub mod text;
#[doc(hidden)]
#[cfg(feature = "macros")]
pub mod exports {
#[cfg(feature = "minidom")]
pub use minidom;
pub use rxml;
/// The built-in `bool` type.
///
/// This is re-exported for use by macros in cases where we cannot rely on
/// people not having done `type bool = str` or some similar shenanigans.
pub type CoreBool = bool;
/// The built-in `u8` type.
///
/// This is re-exported for use by macros in cases where we cannot rely on
/// people not having done `type u8 = str` or some similar shenanigans.
pub type CoreU8 = u8;
}
use alloc::{