Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discriminate between attributes in different namespaces #38

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

tombailo
Copy link

@tombailo tombailo commented Mar 6, 2023

This commit is an alternative approach to fixing #13 (Attribute namespaces are not preserved), inspired by the discussion on #33. It changes the type of the attributes map on the Element struct from a String -> String map to an AttributeName -> String map. AttributeName is a re-export of the xml::name::OwnedName type in xmltree-rs, which is a struct containing the attribute's local name, namespace and prefix.

It is possible to search the attributes map using a fully initialised AttributeName structure, but for convenience get_attribute(), get_mut_attribute() and take_attribute() methods, modelled on the *_element() equivalent are provided, which take an AttributePredicate. Ready-made implementations taking either a local name or local name + namespace are provided.

This is a semver-breaking change, hence version is incremented to 0.11.0, I'm not sure if it is necessary to provide some kind of backward-compatibility compile time switch?

I was in two minds as to how closely to follow the pattern of the existing take_element(), get_element() etc. APIs. While writing the tests I realised that those APIs do not allow you to pass None for the namespace, you can either pass just the local name, and ignore namespace, or local name + namespace. This means it's not possible to explicitly search for elements with no namespace, which seems like a defect to me.

My initial implementation, which I still have on a branch, had implementations of the AttributePredicate trait for String, &str and Cow<&str> just like the the Element equivalents, but having these and an implementation for (N, Option<NS>) to allow the namespace to be specified means that if you want to pass None the type is ambiguous so you have to specify which string-like type you mean, e.g. <Option<String>>::None. This seemed quite awkward so this version only supports &str. I can of course change this if required.

This commit aims to fix eminence#13
(Attribute namespaces are not preserved). It changes the type of the
`attributes` map on the `Element` struct from a String -> String map to
an AttributeName -> String map. AttributeName is a re-export of the
xml::name::OwnedName type in xmltree-rs, which is a struct containing
the attribute's local name, namespace and prefix.

It is possible to search the `attributes` map using a fully initialised
AttributeName structure, but for convenience get_attribute(),
get_mut_attribute() and take_attribute() methods, modelled on the
*_element() equivalent are provided, which take an AttributePredicate.
Ready-made implementations taking either a local name or local name +
namespace are provided.
@tombailo
Copy link
Author

tombailo commented Mar 7, 2023

@ctrlcctrlv I had a go at implementing what Sebastian suggested on your PR, I'd be interested to know if this meets your requirements as well

@@ -22,7 +22,7 @@
//! {
//! // get first `name` element
//! let name = names_element.get_mut_child("name").expect("Can't find name element");
//! name.attributes.insert("suffix".to_owned(), "mr".to_owned());
//! name.attributes.insert(AttributeName::local("suffix"), "mr".to_owned());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we can't somehow wrap AttributeMap (i.e. make it a real struct instead of an alias, even if it's just a newtype struct) which would make this change less painful for downstreams. If we did that, we could (I think, running on fumes here):

trait IntoAttributeKey {
    fn into_attribute_key(self) -> AttributeName;
}

pub struct AttributeMap<K: IntoAttributeKey, V>(AttributeMapInner<K, V>);

impl<S: AsRef<str>> IntoAttributeKey for S {}

That was my main concern with taking a similar approach in #33.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants