xso: add support for overriding names of generated types

In 1265f4b, we introduced a change which may cause a conflict of type
names when deriving the traits on two different types. While a
workaround existed (use `mod`s to isolate the implementation), that is
ugly.

This commit allows overriding the choice of type names.
This commit is contained in:
Jonas Schäfer 2024-07-27 08:52:08 +02:00 committed by Link Mauve
parent eb51b05c13
commit c90752aa51
5 changed files with 77 additions and 12 deletions

View file

@ -587,3 +587,26 @@ fn boxed_child_roundtrip_nested_2() {
};
roundtrip_full::<BoxedChild>("<elem xmlns='urn:example:ns1'><elem><elem/></elem></elem>")
}
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
#[xml(namespace = NS1, name = "elem", builder = RenamedBuilder, iterator = RenamedIter)]
struct RenamedTypes;
#[test]
fn renamed_types_roundtrip() {
#[allow(unused_imports)]
use std::{
option::Option::{None, Some},
result::Result::{Err, Ok},
};
roundtrip_full::<RenamedTypes>("<elem xmlns='urn:example:ns1'/>")
}
#[test]
#[allow(unused_comparisons)]
fn renamed_types_get_renamed() {
// these merely serve as a test that the types are declared with the names
// given in the attributes.
assert!(std::mem::size_of::<RenamedBuilder>() >= 0);
assert!(std::mem::size_of::<RenamedIter>() >= 0);
}

View file

@ -157,6 +157,12 @@ pub(crate) struct XmlCompoundMeta {
/// The debug flag.
pub(crate) debug: Flag,
/// The value assigned to `builder` inside `#[xml(..)]`, if any.
pub(crate) builder: Option<Ident>,
/// The value assigned to `iterator` inside `#[xml(..)]`, if any.
pub(crate) iterator: Option<Ident>,
}
impl XmlCompoundMeta {
@ -167,6 +173,8 @@ impl XmlCompoundMeta {
fn parse_from_attribute(attr: &Attribute) -> Result<Self> {
let mut namespace = None;
let mut name = None;
let mut builder = None;
let mut iterator = None;
let mut debug = Flag::Absent;
attr.parse_nested_meta(|meta| {
@ -188,6 +196,18 @@ impl XmlCompoundMeta {
}
debug = (&meta.path).into();
Ok(())
} else if meta.path.is_ident("builder") {
if builder.is_some() {
return Err(Error::new_spanned(meta.path, "duplicate `builder` key"));
}
builder = Some(meta.value()?.parse()?);
Ok(())
} else if meta.path.is_ident("iterator") {
if iterator.is_some() {
return Err(Error::new_spanned(meta.path, "duplicate `iterator` key"));
}
iterator = Some(meta.value()?.parse()?);
Ok(())
} else {
Err(Error::new_spanned(meta.path, "unsupported key"))
}
@ -198,6 +218,8 @@ impl XmlCompoundMeta {
namespace,
name,
debug,
builder,
iterator,
})
}

View file

@ -85,13 +85,23 @@ impl StructDef {
return Err(Error::new(meta.span, "`name` is required on structs"));
};
let builder_ty_ident = match meta.builder {
Some(v) => v,
None => concat_camel_case(ident, "FromXmlBuilder"),
};
let item_iter_ty_ident = match meta.iterator {
Some(v) => v,
None => concat_camel_case(ident, "AsXmlIterator"),
};
Ok(Self {
namespace,
name,
inner: Compound::from_fields(fields)?,
target_ty_ident: ident.clone(),
builder_ty_ident: concat_camel_case(ident, "FromXmlBuilder"),
item_iter_ty_ident: concat_camel_case(ident, "AsXmlIterator"),
builder_ty_ident,
item_iter_ty_ident,
debug: meta.debug.is_set(),
})
}

View file

@ -2,20 +2,16 @@ Version NEXT:
0000-00-00 Jonas Schäfer <jonas@zombofant.net>
* Breaking
- We now strip trailing underscores from identifiers before constructing
any type names we declare from derive macros. That means that it is
not possible to derive the traits on `Foo` and `Foo_` if both live
in the same scope.
any type names we declare from derive macros.
As a workaround, you can put either of these types into a `mod` and
reexport them from the outer module. The types generated by the derive
macro will then be scoped inside the `mod` and cannot conflict with
the derived types on the other type.
All this is to avoid triggering the camel case lint on the types we
generate.
If you previously derived any of the macros on e.g. `Foo` and `Foo_`
within the same scope, you can use the newly added `builder` and
`iterator` meta keys to override the generated type names.
* Added
- Support for child elements in derive macros. Child elements may also
be wrapped in Option or Box.
- Support for overriding the names of the types generated by the derive
macros.
Version 0.1.2:
2024-07-26 Jonas Schäfer <jonas@zombofant.net>

View file

@ -35,6 +35,7 @@ such:
is also a path.
- *string literal*: A string literal, like `"hello world!"`.
- *type*: A Rust type.
- *ident*: A Rust identifier.
- flag: Has no value. The key's mere presence has relevance and it must not be
followed by a `=` sign.
@ -46,6 +47,8 @@ The following keys are defined on structs:
| --- | --- | --- |
| `namespace` | *string literal* or *path* | The XML element namespace to match. If it is a *path*, it must point at a `&'static str`. |
| `name` | *string literal* or *path* | The XML element name to match. If it is a *path*, it must point at a `&'static NcNameStr`. |
| `builder` | optional *ident* | The name to use for the generated builder type. |
| `iterator` | optional *ident* | The name to use for the generated iterator type. |
Note that the `name` value must be a valid XML element name, without colons.
The namespace prefix, if any, is assigned automatically at serialisation time
@ -58,6 +61,17 @@ and cannot be overridden. The following will thus not compile:
struct Foo;
```
If `builder` or `iterator` are given, the respective generated types will use
the given names instead of names chosen by the derive macro implementation.
Helper types will use these names as prefix. The exact names of helper types
are implementation defined, which is why any type name starting with the
identifiers passed to either of these keys is considered reserved.
By default, the builder type uses the type's name suffixed with
`FromXmlBuilder` and the iterator type uses the type's name suffixed with
`AsXmlIterator`. If the target type has any trailing underscores, they are
removed before making the type name.
### Field meta
For fields, the *meta* consists of a nested meta inside the `#[xml(..)]` meta,