Skip to main content

textlane_html/actions/attribute/
write.rs

1use textlane_core::hast::{Element, PropertyItem, PropertyValue};
2use textlane_core::{Allocator, ArenaVec};
3
4use crate::helpers::property::find_property;
5use crate::helpers::schema::{
6    BOOLEAN, COMMA_OR_SPACE_SEPARATED, COMMA_SEPARATED, HTML, NUMBER, OVERLOADED_BOOLEAN,
7    SPACE_SEPARATED,
8};
9use crate::helpers::value::set_property;
10
11/// Set an attribute by its HTML name, typed as hast types it: `class` becomes a list, a
12/// boolean attribute `true`, replacing one already set.
13pub fn write_attribute<'a>(
14    element: &mut Element<'a>,
15    name: &str,
16    value: &str,
17    alloc: &'a Allocator,
18) {
19    let found = find_property(&HTML, name);
20    let kinds = found.kinds;
21    let value = if kinds & (BOOLEAN | OVERLOADED_BOOLEAN) != 0
22        && (value.is_empty() || value.eq_ignore_ascii_case(&found.attribute))
23    {
24        PropertyValue::Boolean(true)
25    } else if kinds & (SPACE_SEPARATED | COMMA_SEPARATED | COMMA_OR_SPACE_SEPARATED) != 0
26    {
27        let comma = kinds & COMMA_SEPARATED != 0;
28        let mut list = ArenaVec::new_in(alloc);
29        let words: Vec<&str> = match comma {
30            true => value.split(',').map(str::trim).collect(),
31            false => value.split_ascii_whitespace().collect(),
32        };
33        for word in words.into_iter().filter(|word| !word.is_empty()) {
34            list.push(PropertyItem::String(alloc.alloc_str(word)));
35        }
36        PropertyValue::List(list)
37    } else if kinds & NUMBER != 0
38        && let Ok(number) = value.trim().parse::<f64>()
39    {
40        PropertyValue::Number(number)
41    } else {
42        PropertyValue::String(alloc.alloc_str(value))
43    };
44    set_property(element, alloc.alloc_str(&found.property), value);
45}
46
47#[cfg(test)]
48#[path = "write_unit.rs"]
49mod unit;