use crate::types::WebIdlResult;
use oak_idl::ast::{IdlItem, IdlRoot};
pub struct WebIdlTypeChecker {
pub webidl_result: WebIdlResult,
}
impl WebIdlTypeChecker {
pub fn new(webidl_result: WebIdlResult) -> Self {
Self { webidl_result }
}
pub fn check_type_compatibility(&self, ts_type: &str, webidl_type: &str) -> bool {
let type_map = vec![
("string", "DOMString"),
("string", "ByteString"),
("string", "USVString"),
("string", "string"),
("number", "byte"),
("number", "octet"),
("number", "short"),
("number", "unsigned short"),
("number", "long"),
("number", "unsigned long"),
("number", "long long"),
("number", "unsigned long long"),
("number", "float"),
("number", "unrestricted float"),
("number", "double"),
("number", "unrestricted double"),
("number", "integer"),
("number", "unsigned integer"),
("boolean", "boolean"),
("any", "any"),
("undefined", "undefined"),
("null", "null"),
("void", "void"),
];
for (ts, webidl) in type_map {
if ts == ts_type && webidl == webidl_type {
return true;
}
}
if self.check_complex_type_compatibility(ts_type, webidl_type) {
return true;
}
self.check_user_defined_types(ts_type, webidl_type)
}
fn check_complex_type_compatibility(&self, ts_type: &str, webidl_type: &str) -> bool {
if ts_type.starts_with("Array<") && webidl_type.starts_with("sequence<") {
let ts_inner = &ts_type[6..ts_type.len() - 1];
let webidl_inner = &webidl_type[9..webidl_type.len() - 1];
return self.check_type_compatibility(ts_inner, webidl_inner);
}
if ts_type.starts_with("Promise<") && webidl_type.starts_with("Promise<") {
let ts_inner = &ts_type[8..ts_type.len() - 1];
let webidl_inner = &webidl_type[8..webidl_type.len() - 1];
return self.check_type_compatibility(ts_inner, webidl_inner);
}
if ts_type.contains("|") && webidl_type.contains("or") {
let ts_types: Vec<&str> = ts_type.split("|").map(|t| t.trim()).collect();
let webidl_types: Vec<&str> = webidl_type.split("or").map(|t| t.trim()).collect();
if ts_types.len() != webidl_types.len() {
return false;
}
for (ts, webidl) in ts_types.iter().zip(webidl_types.iter()) {
if !self.check_type_compatibility(ts, webidl) {
return false;
}
}
return true;
}
if ts_type.starts_with("{") && ts_type.contains("[key:") && webidl_type.starts_with("record<") {
return true;
}
false
}
fn check_user_defined_types(&self, ts_type: &str, webidl_type: &str) -> bool {
if ts_type == webidl_type {
return true;
}
self.check_type_alias_compatibility(ts_type, webidl_type)
}
fn check_type_alias_compatibility(&self, ts_type: &str, webidl_type: &str) -> bool {
for item in &self.webidl_result.root.items {
if let IdlItem::Typedef(typedef) = item {
if typedef.name == webidl_type {
return self.check_type_compatibility(ts_type, &typedef.type_name);
}
}
}
false
}
pub fn validate_webidl_types(&self) -> bool {
if self.webidl_result.root.items.is_empty() {
return false;
}
self.validate_items(&self.webidl_result.root)
}
fn validate_items(&self, root: &IdlRoot) -> bool {
for item in &root.items {
match item {
IdlItem::Interface(interface) => {
if interface.name.is_empty() {
return false;
}
}
IdlItem::Struct(struct_) => {
if struct_.name.is_empty() {
return false;
}
}
IdlItem::Enum(enum_) => {
if enum_.name.is_empty() {
return false;
}
if enum_.variants.is_empty() {
return false;
}
}
IdlItem::Typedef(typedef) => {
if typedef.name.is_empty() {
return false;
}
}
IdlItem::Const(const_) => {
if const_.name.is_empty() {
return false;
}
}
IdlItem::Module(module) => {
if module.name.is_empty() {
return false;
}
if !self.validate_items(&IdlRoot { items: module.items.clone() }) {
return false;
}
}
_ => {
}
}
}
true
}
}