diff --git a/src/lib.rs b/src/lib.rs index cb6b53d..f0a7076 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,6 +35,7 @@ use rphtml::{ }, }; use std::borrow::Cow; +use std::collections::BTreeMap; use std::rc::Rc; use std::{any::Any, cell::RefCell}; // re export `IAttrValue` `IEnumTyped` `INodeType` @@ -628,6 +629,36 @@ impl IElementTrait for Rc> { None } + fn get_attributes(&self) -> Vec<(String, IAttrValue)> { + let node = &self.borrow(); + let meta = node + .meta + .as_ref() + .expect("Element node must have a meta field."); + let attrs = &meta.borrow().attrs; + let attr_map = meta + .borrow() + .lc_name_map + .iter() + .map(|(name, index)| (*index, name.clone())) + .collect::>(); + attr_map + .into_iter() + .map(|(index, name)| { + let attr = &attrs[index]; + if let Some(value) = &attr.value { + let attr_value = value.content.clone(); + ( + name, + IAttrValue::Value(attr_value.iter().collect(), attr.quote), + ) + } else { + (name, IAttrValue::True) + } + }) + .collect() + } + /// impl `set_attribute` fn set_attribute(&mut self, name: &str, value: Option<&str>) { let mut need_quote = false; diff --git a/src/mesdoc/interface/element.rs b/src/mesdoc/interface/element.rs index 283b412..239ea9f 100644 --- a/src/mesdoc/interface/element.rs +++ b/src/mesdoc/interface/element.rs @@ -296,6 +296,7 @@ pub trait IElementTrait: INodeTrait { fn children_by<'a>(&'a self, matcher: Box); // attribute fn get_attribute(&self, name: &str) -> Option; + fn get_attributes(&self) -> Vec<(String, IAttrValue)>; fn set_attribute(&mut self, name: &str, value: Option<&str>); fn remove_attribute(&mut self, name: &str); fn has_attribute(&self, name: &str) -> bool { diff --git a/tests/attr.rs b/tests/attr.rs index 4fa8615..c675c33 100644 --- a/tests/attr.rs +++ b/tests/attr.rs @@ -6,9 +6,9 @@ type Result = StdResult<(), BoxDynError>; #[test] fn test_normal_attr() -> Result { const ATTR_NAME: &str = "contenteditable"; - const HTML: &str = r#"
"#; + const HTML: &str = r#"
"#; let root = Vis::load(HTML)?; - let mut div = root.children("div"); + let mut div = root.children("div.first"); // has attribute assert!(div.has_attr(ATTR_NAME)); assert!(!div.has_attr("content")); @@ -29,12 +29,31 @@ fn test_normal_attr() -> Result { assert!(value.as_ref().unwrap().to_string() == ""); assert!(value.as_ref().unwrap().to_list().is_empty()); // always get the first appeared attribute - let input = div.children("input"); + let mut input = div.children("input"); let value = input.attr("type"); assert!(value.is_some()); assert!(value.as_ref().unwrap().is_str("text")); assert!(value.as_ref().unwrap().to_string() == "text"); assert_eq!(value.as_ref().unwrap().to_list(), vec!["text"]); + input.set_attr("type", Some("file")); + assert!(input.attr("type").unwrap().is_str("file")); + // attributes + let attrs_div = root.children("div.test-attrs"); + let div = attrs_div.get(0).unwrap(); + let attrs = div.get_attributes(); + assert_eq!(attrs.len(), 4); + let attr_1 = &attrs[0]; + assert_eq!(attr_1.0, String::from("class")); + assert!(attr_1.1.is_str("test-attrs")); + let attr_2 = &attrs[1]; + assert_eq!(attr_2.0, String::from("draggable")); + assert!(attr_2.1.is_true()); + let attr_3 = &attrs[2]; + assert_eq!(attr_3.0, String::from("data-type")); + assert!(attr_3.1.is_str("link")); + let attr_4 = &attrs[3]; + assert_eq!(attr_4.0, String::from("name")); + assert!(attr_4.1.is_str("cool")); // ignore attribute cases: issue #2 let html: &str = r#""#; let root = Vis::load(html)?;