diff options
-rw-r--r-- | src/lib.rs | 102 |
1 files changed, 101 insertions, 1 deletions
diff --git a/src/lib.rs b/src/lib.rs index b766109..55eba4b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -279,7 +279,62 @@ impl Tag { } pub fn get_attribute<'a>(&'a self, key: &str) -> Option<&'a str> { - todo!() + let body = match self.body.as_deref() { + None => return None, + Some(body) => body, + }; + + // get rid of potential self-close + let trimmed = if let Some(suffix) = body.trim().strip_suffix('/') { + suffix + } else { + body.trim() + }; + + let mut wrk = trimmed; + loop { + let key_end_idx = wrk.find(|c: char| c == ' ' || c == '='); + + match key_end_idx { + None => { + // boolean ends body + if wrk == key { + return Some(""); + } else { + break; + } + } + Some(idx) => match &wrk[idx..idx + 1] { + " " => { + // boolean + if &wrk[..idx] == key { + return Some(""); + } else { + wrk = &wrk[idx + 1..]; + } + } + "=" => { + // key-value + let found_name = &wrk[..idx]; + + // we're just assuming the attributes are properly + // formed right now. Skips the `=` and the `"` that + // should be there but we don't check for + wrk = &wrk[idx + 2..]; + let end = wrk.find('"').unwrap(); + let value = &wrk[..end]; + wrk = &wrk[end + 1..].trim_start(); + + if found_name == key { + return Some(value); + } + } + _ => unreachable!(), + }, + } + } + + None } } @@ -437,3 +492,48 @@ mod test { ) } } + +#[cfg(test)] +mod tag_test { + use crate::Tag; + + #[test] + fn tag_finds_boolen_attribute() { + let tag = Tag { + name: "div".into(), + body: Some("contenteditable".into()), + children: vec![], + }; + assert!(tag.get_attribute("contenteditable").is_some()) + } + + #[test] + fn tag_finds_kv_attribute() { + let tag = Tag { + name: "script".into(), + body: Some("src=\"script.js\"".into()), + children: vec![], + }; + assert_eq!(tag.get_attribute("src"), Some("script.js")) + } + + #[test] + fn tag_finds_attribute_with_self_close() { + let tag = Tag { + name: "link".into(), + body: Some("href=\"style.css\" /".into()), + children: vec![], + }; + assert_eq!(tag.get_attribute("href"), Some("style.css")) + } + + #[test] + fn tag_finds_boolean_in_centre() { + let tag = Tag { + name: "div".into(), + body: Some("id=\"divy\" contenteditable style=\"display: none;\" /".into()), + children: vec![], + }; + assert!(tag.get_attribute("contenteditable").is_some()); + } +} |