diff --git a/CHANGELOG.md b/CHANGELOG.md index b808e51dc4..e72b24d216 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [0.3.4] (in development) +### Added + +* Node methods: `get_property_no_ns` (alias: `get_attribute_no_ns`) + ## [0.3.3] 2023-17-07 ### Changed diff --git a/src/readonly/tree.rs b/src/readonly/tree.rs index 8798eac077..f560261dbd 100644 --- a/src/readonly/tree.rs +++ b/src/readonly/tree.rs @@ -251,6 +251,21 @@ impl RoNode { Some(prop_str) } + /// Returns the value of property `name` with no namespace + pub fn get_property_no_ns(self, name: &str) -> Option { + let c_name = CString::new(name).unwrap(); + let value_ptr = unsafe { xmlGetNoNsProp(self.0, c_name.as_bytes().as_ptr()) }; + if value_ptr.is_null() { + return None; + } + let c_value_string = unsafe { CStr::from_ptr(value_ptr as *const c_char) }; + let prop_str = c_value_string.to_string_lossy().into_owned(); + unsafe { + libc::free(value_ptr as *mut c_void); + } + Some(prop_str) + } + /// Return an attribute as a `Node` struct of type AttributeNode pub fn get_property_node(self, name: &str) -> Option { let c_name = CString::new(name).unwrap(); @@ -264,11 +279,17 @@ impl RoNode { pub fn get_attribute(self, name: &str) -> Option { self.get_property(name) } + /// Alias for get_property_ns pub fn get_attribute_ns(self, name: &str, ns: &str) -> Option { self.get_property_ns(name, ns) } + /// Alias for get_property_no_ns + pub fn get_attribute_no_ns(self, name: &str) -> Option { + self.get_property_no_ns(name) + } + /// Alias for get_property_node pub fn get_attribute_node(self, name: &str) -> Option { self.get_property_node(name) diff --git a/src/tree/node.rs b/src/tree/node.rs index b71b33dcc9..f3050e7157 100644 --- a/src/tree/node.rs +++ b/src/tree/node.rs @@ -457,6 +457,21 @@ impl Node { Some(prop_str) } + /// Returns the value of property `name` with no namespace + pub fn get_property_no_ns(&self, name: &str) -> Option { + let c_name = CString::new(name).unwrap(); + let value_ptr = unsafe { xmlGetNoNsProp(self.node_ptr(), c_name.as_bytes().as_ptr()) }; + if value_ptr.is_null() { + return None; + } + let c_value_string = unsafe { CStr::from_ptr(value_ptr as *const c_char) }; + let prop_str = c_value_string.to_string_lossy().into_owned(); + unsafe { + libc::free(value_ptr as *mut c_void); + } + Some(prop_str) + } + /// Return an attribute as a `Node` struct of type AttributeNode pub fn get_property_node(&self, name: &str) -> Option { let c_name = CString::new(name).unwrap(); @@ -590,11 +605,17 @@ impl Node { pub fn get_attribute(&self, name: &str) -> Option { self.get_property(name) } + /// Alias for get_property_ns pub fn get_attribute_ns(&self, name: &str, ns: &str) -> Option { self.get_property_ns(name, ns) } + /// Alias for get_property_no_ns + pub fn get_attribute_no_ns(&self, name: &str) -> Option { + self.get_property_no_ns(name) + } + /// Alias for get_property_node pub fn get_attribute_node(&self, name: &str) -> Option { self.get_property_node(name) diff --git a/tests/tree_tests.rs b/tests/tree_tests.rs index 101d368ee9..1d99bfadc8 100644 --- a/tests/tree_tests.rs +++ b/tests/tree_tests.rs @@ -167,6 +167,47 @@ fn attribute_namespace_accessors() { assert_eq!(fb_uri, Some("http://www.foobar.org".to_string())); // system ns has the global prefix when doing global lookup } +#[test] +fn attribute_no_namespace() { + let mut doc = Document::new().unwrap(); + let element_result = Node::new("example", None, &doc); + assert!(element_result.is_ok()); + + let mut element = element_result.unwrap(); + doc.set_root_element(&element); + + let ns_result = Namespace::new("myns", "https://www.example.com/myns", &mut element); + assert!(ns_result.is_ok()); + let ns = ns_result.unwrap(); + assert!(element.set_attribute_ns("foo", "ns", &ns).is_ok()); + + let foo_ns_attr = element.get_attribute_ns("foo", "https://www.example.com/myns"); + assert!(foo_ns_attr.is_some()); + assert_eq!(foo_ns_attr.unwrap(), "ns"); + + let foo_no_ns_attr = element.get_attribute_no_ns("foo"); + assert!(foo_no_ns_attr.is_none()); + + assert!(element.set_attribute("foo", "no_ns").is_ok()); + + let foo_no_ns_attr = element.get_attribute_no_ns("foo"); + 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.set_attribute("bar", "bar").is_ok()); + let bar_no_ns_attr = element.get_attribute_no_ns("bar"); + assert!(bar_no_ns_attr.is_some()); + assert_eq!(bar_no_ns_attr.unwrap(), "bar"); +} + #[test] fn node_can_unbind() { let mut doc = Document::new().unwrap();