It is not necessary anymore, because we switched from `IntoXml` to
`AsXml`, allowing `transform` to work with a reference instead of
consuming its input.
Before that, `try_from_element` was the only way to fallibly attempt to
parse something from `Element` without having to clone the entire DOM.
This was a bit tricky to build, because it is possible to have an
indirection through a `static` there. Thanks to Rust's extensive
const-fn capabilities, though, it's in fact possible to cover all cases.
We still do two different checks to improve user experience. If we can,
from within the proc macro, determine that two fields refer to the same
XML attribute (because their namespace/name values use the same Rust
tokens), then we reject the fields with a clear error message pointing
at both fields.
In the other case, when there's e.g. `#[xml(lang)]` and
`#[xml(attribute(namespace = rxml::XMLNS_XML, name = "lang"))]`, the
macro cannot be sure that XMLNS_XML is in fact the XML namespace. For
that case, we generate code which is evaluated at compile time (and
has no runtime impact) which panics if the namespace and name of two
attribute-matching fields is the same.
The error message will be less clear (because it contains extra,
unchangeable wording like "evaluation of constant value failed" and "the
evaluated program panicked at", which may be a bit confusing) than the
message generated by the macros themselves, but it's a price we have to
pay unfortunately.
Note that this check may seem cosmetic and purely for better user
experience, but it is in fact needed to avoid generating not-well-formed
and/or not-namespace-well-formed XML: As `AsXml` generates `xso::Item`,
where each attribute is emitted separated (and not aggregated in a
map structure), a naive (and efficient) implementation of a writer might
not double-check that no duplicate attributes are generated.
Because this attribute may occur in random places, it makes no sense
failing on it. We discard it after attribute processing though, so it
can still be captured by structs which are explicitly interested in it.
This is still far from being a full fix. Still, we can avoid the lang
attribute (by allowing discarding the `xml:lang` value via the feature
introduced a couple commits ago) and we can avoid emitting `xml:lang=""`
where no language was previously set using the mechanism introduced for
<message/> in 5172fb5e.
The test case which is added fails to compile unless one puts the
`parent` field before the `id` field. The cause is explained somewhat by
the change, but I'll spell it out here nontheless.
Previously, the loop in `Compound::make_as_item_iter_statemachine`
assumed that the serialisation order of fields would match their
declaration order. That is not generally true: attributes must be
serialised before element content, because they must be emitted before
the element header is closed.
This change thus splits the generated states into "header" states (for
everything before the end of the element header (think `>`)) and
"body" states (for everything after and including the end of the
element header). After all fields have been processed, we can then
add the data fields of the body fields to the header states so that
they are carried through the generated state machine until they are
needed in the body.
Separate from the macro_tests module because it doesn't actually test
macros, it uses it as a convenience.
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
The idna crate validates against UTS 46, which supports domains using
either IDNA2003 or IDNA2008. This allows us to support both old and
new internationalized domain names.
This dependency isn’t a new one in the tree, as hickory-proto, url and
tokio-xmpp all were already depending on it.
There are a bunch of other checks that have to be performed, this is
inspired by slixmpp’s JID implementation.
This additionally should optimize the parsing a tiny bit by looking for
both @ and / at the same time and iterating on them, instead of one by
one.
Thanks nicoco for the report!