Parse property definitions from a CSS spec HTML
(html: &str)
| 18 | |
| 19 | /// Parse property definitions from a CSS spec HTML |
| 20 | pub fn parse_spec_properties(html: &str) -> Result<Vec<PropertyDefinition>> { |
| 21 | let document = Html::parse_document(html); |
| 22 | let table_selector = Selector::parse("table.propdef, table.descdef").unwrap(); |
| 23 | let tr_selector = Selector::parse("tr").unwrap(); |
| 24 | let th_selector = Selector::parse("th").unwrap(); |
| 25 | let td_selector = Selector::parse("td").unwrap(); |
| 26 | |
| 27 | let mut properties = Vec::new(); |
| 28 | |
| 29 | for table in document.select(&table_selector) { |
| 30 | let mut prop_data: HashMap<String, String> = HashMap::new(); |
| 31 | for row in table.select(&tr_selector) { |
| 32 | let headers: Vec<_> = row.select(&th_selector).collect(); |
| 33 | let data: Vec<_> = row.select(&td_selector).collect(); |
| 34 | |
| 35 | if headers.len() == 1 && data.len() == 1 { |
| 36 | let key = headers[0].text().collect::<String>().trim().to_lowercase(); |
| 37 | let key = key.trim_end_matches(':').to_string(); |
| 38 | let value = data[0].text().collect::<String>().trim().to_string(); |
| 39 | prop_data.insert(key, value); |
| 40 | } |
| 41 | } |
| 42 | |
| 43 | if let Some(names) = prop_data.get("name") { |
| 44 | for name in names.split(',') { |
| 45 | let name = name.trim().to_string(); |
| 46 | |
| 47 | // Skip if: |
| 48 | // - this has "new values" - it means it's extending an existing property |
| 49 | // - this has "for" - it means it's for an @-rule and not a general style value |
| 50 | // TODO: We should be smarter about these, but for now we can simply skip |
| 51 | if prop_data.contains_key("new values") || prop_data.contains_key("for") { |
| 52 | continue; |
| 53 | } |
| 54 | |
| 55 | if let Some(value) = prop_data.get("value") { |
| 56 | properties.push(PropertyDefinition { |
| 57 | name: name.clone(), |
| 58 | value: value.clone(), |
| 59 | initial: prop_data.get("initial").cloned().unwrap_or_default(), |
| 60 | applies_to: prop_data.get("applies to").cloned().unwrap_or_default(), |
| 61 | inherited: prop_data.get("inherited").cloned().unwrap_or_default(), |
| 62 | percentages: prop_data.get("percentages").cloned().unwrap_or_default(), |
| 63 | animation_type: prop_data |
| 64 | .get("animation type") |
| 65 | .or_else(|| prop_data.get("animatable")) |
| 66 | .cloned(), |
| 67 | computed_value: prop_data.get("computed value").or_else(|| prop_data.get("computed")).cloned(), |
| 68 | canonical_order: prop_data.get("canonical order").cloned(), |
| 69 | logical_property_group: prop_data.get("logical property group").cloned(), |
| 70 | }); |
| 71 | } |
| 72 | } |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | Ok(properties) |
| 77 | } |
no test coverage detected