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:
parent
eb51b05c13
commit
c90752aa51
5 changed files with 77 additions and 12 deletions
|
|
@ -587,3 +587,26 @@ fn boxed_child_roundtrip_nested_2() {
|
||||||
};
|
};
|
||||||
roundtrip_full::<BoxedChild>("<elem xmlns='urn:example:ns1'><elem><elem/></elem></elem>")
|
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);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,12 @@ pub(crate) struct XmlCompoundMeta {
|
||||||
|
|
||||||
/// The debug flag.
|
/// The debug flag.
|
||||||
pub(crate) 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 {
|
impl XmlCompoundMeta {
|
||||||
|
|
@ -167,6 +173,8 @@ impl XmlCompoundMeta {
|
||||||
fn parse_from_attribute(attr: &Attribute) -> Result<Self> {
|
fn parse_from_attribute(attr: &Attribute) -> Result<Self> {
|
||||||
let mut namespace = None;
|
let mut namespace = None;
|
||||||
let mut name = None;
|
let mut name = None;
|
||||||
|
let mut builder = None;
|
||||||
|
let mut iterator = None;
|
||||||
let mut debug = Flag::Absent;
|
let mut debug = Flag::Absent;
|
||||||
|
|
||||||
attr.parse_nested_meta(|meta| {
|
attr.parse_nested_meta(|meta| {
|
||||||
|
|
@ -188,6 +196,18 @@ impl XmlCompoundMeta {
|
||||||
}
|
}
|
||||||
debug = (&meta.path).into();
|
debug = (&meta.path).into();
|
||||||
Ok(())
|
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 {
|
} else {
|
||||||
Err(Error::new_spanned(meta.path, "unsupported key"))
|
Err(Error::new_spanned(meta.path, "unsupported key"))
|
||||||
}
|
}
|
||||||
|
|
@ -198,6 +218,8 @@ impl XmlCompoundMeta {
|
||||||
namespace,
|
namespace,
|
||||||
name,
|
name,
|
||||||
debug,
|
debug,
|
||||||
|
builder,
|
||||||
|
iterator,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -85,13 +85,23 @@ impl StructDef {
|
||||||
return Err(Error::new(meta.span, "`name` is required on structs"));
|
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 {
|
Ok(Self {
|
||||||
namespace,
|
namespace,
|
||||||
name,
|
name,
|
||||||
inner: Compound::from_fields(fields)?,
|
inner: Compound::from_fields(fields)?,
|
||||||
target_ty_ident: ident.clone(),
|
target_ty_ident: ident.clone(),
|
||||||
builder_ty_ident: concat_camel_case(ident, "FromXmlBuilder"),
|
builder_ty_ident,
|
||||||
item_iter_ty_ident: concat_camel_case(ident, "AsXmlIterator"),
|
item_iter_ty_ident,
|
||||||
debug: meta.debug.is_set(),
|
debug: meta.debug.is_set(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,20 +2,16 @@ Version NEXT:
|
||||||
0000-00-00 Jonas Schäfer <jonas@zombofant.net>
|
0000-00-00 Jonas Schäfer <jonas@zombofant.net>
|
||||||
* Breaking
|
* Breaking
|
||||||
- We now strip trailing underscores from identifiers before constructing
|
- We now strip trailing underscores from identifiers before constructing
|
||||||
any type names we declare from derive macros. That means that it is
|
any type names we declare from derive macros.
|
||||||
not possible to derive the traits on `Foo` and `Foo_` if both live
|
|
||||||
in the same scope.
|
|
||||||
|
|
||||||
As a workaround, you can put either of these types into a `mod` and
|
If you previously derived any of the macros on e.g. `Foo` and `Foo_`
|
||||||
reexport them from the outer module. The types generated by the derive
|
within the same scope, you can use the newly added `builder` and
|
||||||
macro will then be scoped inside the `mod` and cannot conflict with
|
`iterator` meta keys to override the generated type names.
|
||||||
the derived types on the other type.
|
|
||||||
|
|
||||||
All this is to avoid triggering the camel case lint on the types we
|
|
||||||
generate.
|
|
||||||
* Added
|
* Added
|
||||||
- Support for child elements in derive macros. Child elements may also
|
- Support for child elements in derive macros. Child elements may also
|
||||||
be wrapped in Option or Box.
|
be wrapped in Option or Box.
|
||||||
|
- Support for overriding the names of the types generated by the derive
|
||||||
|
macros.
|
||||||
|
|
||||||
Version 0.1.2:
|
Version 0.1.2:
|
||||||
2024-07-26 Jonas Schäfer <jonas@zombofant.net>
|
2024-07-26 Jonas Schäfer <jonas@zombofant.net>
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ such:
|
||||||
is also a path.
|
is also a path.
|
||||||
- *string literal*: A string literal, like `"hello world!"`.
|
- *string literal*: A string literal, like `"hello world!"`.
|
||||||
- *type*: A Rust type.
|
- *type*: A Rust type.
|
||||||
|
- *ident*: A Rust identifier.
|
||||||
- flag: Has no value. The key's mere presence has relevance and it must not be
|
- flag: Has no value. The key's mere presence has relevance and it must not be
|
||||||
followed by a `=` sign.
|
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`. |
|
| `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`. |
|
| `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.
|
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
|
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;
|
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
|
### Field meta
|
||||||
|
|
||||||
For fields, the *meta* consists of a nested meta inside the `#[xml(..)]` meta,
|
For fields, the *meta* consists of a nested meta inside the `#[xml(..)]` meta,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue