Skip to content

Commit

Permalink
Add no_ns variants for Node and RoNode methods
Browse files Browse the repository at this point in the history
Add has_property_no_ns, remove_property_no_ns and get_property_node_no_ns

Add get_property_node_ns
  • Loading branch information
anwaralameddin authored and dginev committed Mar 12, 2024
1 parent 6dd1af3 commit 0dcce40
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 23 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

### Added

* Node methods: `get_property_no_ns` (alias: `get_attribute_no_ns`), `get_properties_ns` (alias: `get_attributes_ns`)
* Node methods: `get_property_no_ns` (alias: `get_attribute_no_ns`), `get_properties_ns` (alias: `get_attributes_ns`), `has_property_no_ns` (alias: `has_attribute_no_ns`), `remove_property_no_ns` (alias: `remove_attribute_no_ns`), `get_property_node_ns` (alias: `get_attribute_node_ns`), `get_property_node_no_ns` (alias: `get_attribute_node_no_ns`)
* Added implementations of `Hash`, `PartialEq` and `Eq` traits for `Namespace`

## [0.3.3] 2023-17-07
Expand Down
41 changes: 41 additions & 0 deletions src/readonly/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,22 @@ impl RoNode {
}
}

/// Return an attribute in a namespace `ns` as a `Node` of type AttributeNode
pub fn get_property_node_ns(self, name: &str, ns: &str) -> Option<RoNode> {
let c_name = CString::new(name).unwrap();
let c_ns = CString::new(ns).unwrap();
let attr_node =
unsafe { xmlHasNsProp(self.0, c_name.as_bytes().as_ptr(), c_ns.as_bytes().as_ptr()) };
self.ptr_as_option(attr_node as xmlNodePtr)
}

/// Return an attribute with no namespace as a `Node` of type AttributeNode
pub fn get_property_node_no_ns(self, name: &str) -> Option<RoNode> {
let c_name = CString::new(name).unwrap();
let attr_node = unsafe { xmlHasNsProp(self.0, c_name.as_bytes().as_ptr(), ptr::null()) };
self.ptr_as_option(attr_node as xmlNodePtr)
}

/// Alias for get_property
pub fn get_attribute(self, name: &str) -> Option<String> {
self.get_property(name)
Expand All @@ -295,6 +311,16 @@ impl RoNode {
self.get_property_node(name)
}

/// Alias for get_property_node_ns
pub fn get_attribute_node_ns(self, name: &str, ns: &str) -> Option<RoNode> {
self.get_property_node_ns(name, ns)
}

/// Alias for get_property_node_no_ns
pub fn get_attribute_node_no_ns(self, name: &str) -> Option<RoNode> {
self.get_property_node_no_ns(name)
}

/// Get a copy of the attributes of this node
pub fn get_properties(self) -> HashMap<String, String> {
let mut attributes = HashMap::new();
Expand Down Expand Up @@ -354,6 +380,7 @@ impl RoNode {
let value_ptr = unsafe { xmlHasProp(self.0, c_name.as_bytes().as_ptr()) };
!value_ptr.is_null()
}

/// Check if property `name` in namespace `ns` exists
pub fn has_property_ns(self, name: &str, ns: &str) -> bool {
let c_name = CString::new(name).unwrap();
Expand All @@ -362,15 +389,29 @@ impl RoNode {
unsafe { xmlHasNsProp(self.0, c_name.as_bytes().as_ptr(), c_ns.as_bytes().as_ptr()) };
!value_ptr.is_null()
}

/// Check if property `name` with no namespace exists
pub fn has_property_no_ns(self, name: &str) -> bool {
let c_name = CString::new(name).unwrap();
let value_ptr = unsafe { xmlHasNsProp(self.0, c_name.as_bytes().as_ptr(), ptr::null()) };
!value_ptr.is_null()
}

/// Alias for has_property
pub fn has_attribute(self, name: &str) -> bool {
self.has_property(name)
}

/// Alias for has_property_ns
pub fn has_attribute_ns(self, name: &str, ns: &str) -> bool {
self.has_property_ns(name, ns)
}

/// Alias for has_property_no_ns
pub fn has_attribute_no_ns(self, name: &str) -> bool {
self.has_property_no_ns(name)
}

/// Gets the active namespace associated of this node
pub fn get_namespace(self) -> Option<Namespace> {
let ns_ptr = xmlNodeNs(self.0);
Expand Down
79 changes: 79 additions & 0 deletions src/tree/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -481,12 +481,35 @@ impl Node {
}
}

/// Return an attribute in a namespace `ns` as a `Node` of type AttributeNode
pub fn get_property_node_ns(&self, name: &str, ns: &str) -> Option<Node> {
let c_name = CString::new(name).unwrap();
let c_ns = CString::new(ns).unwrap();
let attr_node = unsafe {
xmlHasNsProp(
self.node_ptr(),
c_name.as_bytes().as_ptr(),
c_ns.as_bytes().as_ptr(),
)
};
self.ptr_as_option(attr_node as xmlNodePtr)
}

/// Return an attribute with no namespace as a `Node` of type AttributeNode
pub fn get_property_node_no_ns(&self, name: &str) -> Option<Node> {
let c_name = CString::new(name).unwrap();
let attr_node =
unsafe { xmlHasNsProp(self.node_ptr(), c_name.as_bytes().as_ptr(), ptr::null()) };
self.ptr_as_option(attr_node as xmlNodePtr)
}

/// Check if a property has been defined, without allocating its value
pub fn has_property(&self, name: &str) -> bool {
let c_name = CString::new(name).unwrap();
let value_ptr = unsafe { xmlHasProp(self.node_ptr(), c_name.as_bytes().as_ptr()) };
!value_ptr.is_null()
}

/// Check if property `name` in namespace `ns` exists
pub fn has_property_ns(&self, name: &str, ns: &str) -> bool {
let c_name = CString::new(name).unwrap();
Expand All @@ -500,6 +523,15 @@ impl Node {
};
!value_ptr.is_null()
}

/// Check if property `name` with no namespace exists
pub fn has_property_no_ns(&self, name: &str) -> bool {
let c_name = CString::new(name).unwrap();
let value_ptr =
unsafe { xmlHasNsProp(self.node_ptr(), c_name.as_bytes().as_ptr(), ptr::null()) };
!value_ptr.is_null()
}

/// Alias for has_property
pub fn has_attribute(&self, name: &str) -> bool {
self.has_property(name)
Expand All @@ -509,6 +541,11 @@ impl Node {
self.has_property_ns(name, ns)
}

/// Alias for has_property_no_ns
pub fn has_attribute_no_ns(&self, name: &str) -> bool {
self.has_property_no_ns(name)
}

/// Sets the value of property `name` to `value`
pub fn set_property(
&mut self,
Expand Down Expand Up @@ -601,6 +638,33 @@ impl Node {
}
}

/// Removes the property of given `name` with no namespace
pub fn remove_property_no_ns(&mut self, name: &str) -> Result<(), Box<dyn Error + Send + Sync>> {
let c_name = CString::new(name).unwrap();
let attr_node = unsafe {
xmlHasNsProp(
self.node_ptr_mut()?,
c_name.as_bytes().as_ptr(),
ptr::null(),
)
};
if !attr_node.is_null() {
let remove_prop_status = unsafe { xmlRemoveProp(attr_node) };
if remove_prop_status == 0 {
Ok(())
} else {
// Propagate libxml2 failure to remove
Err(From::from(format!(
"libxml2 failed to remove property with status: {:?}",
remove_prop_status
)))
}
} else {
// silently no-op if asked to remove a property which is not present
Ok(())
}
}

/// Alias for get_property
pub fn get_attribute(&self, name: &str) -> Option<String> {
self.get_property(name)
Expand All @@ -621,6 +685,16 @@ impl Node {
self.get_property_node(name)
}

/// Alias for get_property_node_ns
pub fn get_attribute_node_ns(&self, name: &str, ns: &str) -> Option<Node> {
self.get_property_node_ns(name, ns)
}

/// Alias for get_property_node_no_ns
pub fn get_attribute_node_no_ns(&self, name: &str) -> Option<Node> {
self.get_property_node_no_ns(name)
}

/// Alias for set_property
pub fn set_attribute(
&mut self,
Expand Down Expand Up @@ -653,6 +727,11 @@ impl Node {
self.remove_property_ns(name, ns)
}

/// Alias for remove_property_no_ns
pub fn remove_attribute_no_ns(&mut self, name: &str) -> Result<(), Box<dyn Error + Send + Sync>> {
self.remove_property_no_ns(name)
}

/// Get a copy of the attributes of this node
pub fn get_properties(&self) -> HashMap<String, String> {
let mut attributes = HashMap::new();
Expand Down
49 changes: 27 additions & 22 deletions tests/tree_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,12 @@ fn node_attributes_ns_accessor() {
);

// Has
// TODO include this when `has_attribute_no_ns` is implemented
// assert!(child.has_attribute("attribute"));
assert!(child.has_attribute("attribute"));
assert!(child.has_attribute_no_ns("attribute"));
assert!(child.has_attribute_ns("attribute", "http://www.example.com/myns"),);
assert!(child.has_attribute("attr"));
assert!(!child.has_attribute_no_ns("attr"));
assert!(child.has_attribute_ns("attr", "http://www.example.com/myns"));

// Get
assert_eq!(
Expand All @@ -162,13 +166,18 @@ fn node_attributes_ns_accessor() {
);

// Get as node
// TODO include this when `get_attribute_node_ns` and
// `get_attribute_node_no_ns` are implemented
// let attr_node_opt = child.get_attribute_node("attribute");
// assert!(attr_node_opt.is_some());
// let attr_node = attr_node_opt.unwrap();
// assert_eq!(attr_node.get_name(), "attribute");
// assert_eq!(attr_node.get_type(), Some(NodeType::AttributeNode));
let attr_node_opt = child.get_attribute_node_no_ns("attribute");
assert!(attr_node_opt.is_some());
let attr_node = attr_node_opt.unwrap();
assert_eq!(attr_node.get_name(), "attribute");
assert_eq!(attr_node.get_type(), Some(NodeType::AttributeNode));
let attr_node_opt = child.get_attribute_node_no_ns("attr");
assert!(attr_node_opt.is_none());
let attr_node_opt = child.get_attribute_node_ns("attr", "http://www.example.com/myns");
assert!(attr_node_opt.is_some());
let attr_node = attr_node_opt.unwrap();
assert_eq!(attr_node.get_name(), "attr");
assert_eq!(attr_node.get_type(), Some(NodeType::AttributeNode));

// Set
assert!(child.set_attribute("attribute", "setter_value").is_ok());
Expand All @@ -184,13 +193,13 @@ fn node_attributes_ns_accessor() {
Some("setter_value".to_string())
);
// Remove
// TODO include this when `remove_attribute_no_ns` is implemented
// assert!(child.remove_attribute("attribute").is_ok());
// assert_eq!(child.get_attribute("attribute"), None);
// assert_eq!(child.has_attribute("attribute"), false);
assert!(child.has_attribute_no_ns("attribute"));
assert!(child.remove_attribute_no_ns("attribute").is_ok());
assert_eq!(child.get_attribute_no_ns("attribute"), None);
assert!(!child.has_attribute_no_ns("attribute"));
// Recount
// let attributes = child.get_attributes_ns();
// assert_eq!(attributes.len(), 2);
let attributes = child.get_attributes_ns();
assert_eq!(attributes.len(), 2);
}

#[test]
Expand Down Expand Up @@ -321,13 +330,9 @@ fn attribute_no_namespace() {
assert!(foo_no_ns_attr.is_some());
assert_eq!(foo_no_ns_attr.unwrap(), "no_ns");

// TODO: include this when `remove_attribute_no_ns` is implemented
// It's not possible use remove_attribute here as it removes the first
// attribute found with the local name regardless of the namespace; here it
// removes the attribute with the namespace
// assert!(element.remove_attribute_no_ns("foo").is_ok());
// let foo_no_ns_attr = element.get_attribute_no_ns("foo");
// assert!(foo_no_ns_attr.is_none());
assert!(element.remove_attribute_no_ns("foo").is_ok());
let foo_no_ns_attr = element.get_attribute_no_ns("foo");
assert!(foo_no_ns_attr.is_none());

assert!(element.set_attribute("bar", "bar").is_ok());
let bar_no_ns_attr = element.get_attribute_no_ns("bar");
Expand Down

0 comments on commit 0dcce40

Please sign in to comment.