kozan_core/dom/
attribute.rs1#[derive(Clone, Debug, PartialEq, Eq)]
9pub struct Attribute {
10 name: String,
11 value: String,
12}
13
14impl Attribute {
15 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
17 Self {
18 name: name.into(),
19 value: value.into(),
20 }
21 }
22
23 #[inline]
25 #[must_use]
26 pub fn name(&self) -> &str {
27 &self.name
28 }
29
30 #[inline]
32 #[must_use]
33 pub fn value(&self) -> &str {
34 &self.value
35 }
36
37 pub fn set_value(&mut self, value: impl Into<String>) -> String {
39 core::mem::replace(&mut self.value, value.into())
40 }
41}
42
43#[derive(Clone, Debug, Default)]
48pub struct AttributeCollection {
49 attrs: Vec<Attribute>,
50}
51
52impl AttributeCollection {
53 #[must_use]
55 pub fn new() -> Self {
56 Self { attrs: Vec::new() }
57 }
58
59 #[must_use]
61 pub fn get(&self, name: &str) -> Option<&str> {
62 self.attrs
63 .iter()
64 .find(|a| a.name == name)
65 .map(|a| a.value.as_str())
66 }
67
68 pub fn set(&mut self, name: &str, value: impl Into<String>) {
70 if let Some(attr) = self.attrs.iter_mut().find(|a| a.name == name) {
71 attr.value = value.into();
72 } else {
73 self.attrs.push(Attribute::new(name, value));
74 }
75 }
76
77 pub fn remove(&mut self, name: &str) -> Option<String> {
79 if let Some(pos) = self.attrs.iter().position(|a| a.name == name) {
80 Some(self.attrs.swap_remove(pos).value)
81 } else {
82 None
83 }
84 }
85
86 #[must_use]
88 pub fn has(&self, name: &str) -> bool {
89 self.attrs.iter().any(|a| a.name == name)
90 }
91
92 #[inline]
94 #[must_use]
95 pub fn len(&self) -> usize {
96 self.attrs.len()
97 }
98
99 #[inline]
101 #[must_use]
102 pub fn is_empty(&self) -> bool {
103 self.attrs.is_empty()
104 }
105
106 pub fn iter(&self) -> impl Iterator<Item = &Attribute> {
108 self.attrs.iter()
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115
116 #[test]
117 fn empty_collection() {
118 let attrs = AttributeCollection::new();
119 assert!(attrs.is_empty());
120 assert_eq!(attrs.len(), 0);
121 assert!(attrs.get("id").is_none());
122 }
123
124 #[test]
125 fn set_and_get() {
126 let mut attrs = AttributeCollection::new();
127 attrs.set("id", "main");
128 assert_eq!(attrs.get("id"), Some("main"));
129 assert_eq!(attrs.len(), 1);
130 }
131
132 #[test]
133 fn set_overwrites() {
134 let mut attrs = AttributeCollection::new();
135 attrs.set("id", "old");
136 attrs.set("id", "new");
137 assert_eq!(attrs.get("id"), Some("new"));
138 assert_eq!(attrs.len(), 1);
139 }
140
141 #[test]
142 fn remove_returns_old_value() {
143 let mut attrs = AttributeCollection::new();
144 attrs.set("class", "container");
145 let old = attrs.remove("class");
146 assert_eq!(old, Some("container".to_string()));
147 assert!(attrs.is_empty());
148 }
149
150 #[test]
151 fn remove_nonexistent_returns_none() {
152 let mut attrs = AttributeCollection::new();
153 assert_eq!(attrs.remove("missing"), None);
154 }
155
156 #[test]
157 fn has_check() {
158 let mut attrs = AttributeCollection::new();
159 attrs.set("data-x", "1");
160 assert!(attrs.has("data-x"));
161 assert!(!attrs.has("data-y"));
162 }
163
164 #[test]
165 fn multiple_attributes() {
166 let mut attrs = AttributeCollection::new();
167 attrs.set("id", "test");
168 attrs.set("class", "foo bar");
169 attrs.set("data-value", "42");
170 assert_eq!(attrs.len(), 3);
171 assert_eq!(attrs.get("id"), Some("test"));
172 assert_eq!(attrs.get("class"), Some("foo bar"));
173 assert_eq!(attrs.get("data-value"), Some("42"));
174 }
175
176 #[test]
177 fn iterate_attributes() {
178 let mut attrs = AttributeCollection::new();
179 attrs.set("a", "1");
180 attrs.set("b", "2");
181 let names: Vec<&str> = attrs.iter().map(|a| a.name()).collect();
182 assert_eq!(names.len(), 2);
183 assert!(names.contains(&"a"));
184 assert!(names.contains(&"b"));
185 }
186}