html_bindgen/parse/
webidls.rs

1use std::path::PathBuf;
2
3use super::{Attribute, AttributeType};
4use crate::Result;
5use convert_case::{Case, Casing};
6use serde::{Deserialize, Serialize};
7use weedle::interface::InterfaceMember;
8
9/// The parsed WebIDL definitions converted from the raw spec.
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct ParsedInterface {
12    pub name: String,
13    pub inherits_from: Option<String>,
14    pub attributes: Vec<Attribute>,
15}
16
17pub fn parse_webidls(
18    iter: impl Iterator<Item = Result<(String, PathBuf)>>,
19) -> Result<Vec<ParsedInterface>> {
20    let mut outputs = vec![];
21    for res in iter {
22        let (string, path) = res?;
23        let filename = path.file_name().unwrap().to_str().unwrap();
24        if !filename.starts_with("HTML") {
25            continue;
26        }
27        let string = string.trim();
28        let definitions = weedle::parse(&string).map_err(|err| err.to_string())?;
29        let definitions = definitions.into_iter();
30        for def in definitions {
31            if let weedle::Definition::Interface(interface) = def {
32                outputs.push(ParsedInterface {
33                    name: parse_interface_name(&interface),
34                    inherits_from: parse_inheritance(&interface),
35                    attributes: interface
36                        .members
37                        .body
38                        .iter()
39                        .filter_map(parse_attributes)
40                        .collect::<Vec<_>>(),
41                });
42            }
43        }
44    }
45    Ok(outputs)
46}
47
48fn parse_interface_name(interface: &weedle::InterfaceDefinition) -> String {
49    interface.identifier.0.to_owned()
50}
51
52fn parse_inheritance(interface: &weedle::InterfaceDefinition) -> Option<String> {
53    interface
54        .inheritance
55        .map(|parent| parent.identifier.0.to_string())
56}
57
58fn parse_attributes(member: &InterfaceMember) -> Option<Attribute> {
59    if let InterfaceMember::Attribute(attr) = member {
60        // NOTE: we're skipping over all DOM-only methods for now.
61        if attr.readonly.is_some() {
62            return None;
63        }
64
65        let ty = match &attr.type_.type_ {
66            weedle::types::Type::Single(ty) => match ty {
67                weedle::types::SingleType::NonAny(ty) => match ty {
68                    weedle::types::NonAnyType::Integer(_) => AttributeType::Integer,
69                    weedle::types::NonAnyType::FloatingPoint(_) => AttributeType::Float,
70                    weedle::types::NonAnyType::Boolean(_) => AttributeType::Bool,
71                    weedle::types::NonAnyType::Object(_) => {
72                        // `js-sys` doesn't handle this either, so we just skip right past it.
73                        return None;
74                    }
75                    weedle::types::NonAnyType::USVString(_)
76                    | weedle::types::NonAnyType::DOMString(_) => AttributeType::String,
77                    weedle::types::NonAnyType::Identifier(id) => {
78                        AttributeType::Identifier(id.type_.0.to_owned())
79                    }
80                    ty => unreachable!("{ty:?} is not a recognized type"),
81                },
82                weedle::types::SingleType::Any(_) => return None,
83            },
84            _ => return None,
85        };
86        let field_name = attr.identifier.0.to_string().to_case(Case::Snake);
87        let field_name = super::normalize_field_name(&field_name);
88        Some(Attribute {
89            name: attr.identifier.0.to_string(),
90            field_name,
91            description: String::new(),
92            ty,
93        })
94    } else {
95        None
96    }
97}