about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/lib.rs102
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());
+	}
+}