diff --git a/crates/webidl/src/idl_type.rs b/crates/webidl/src/idl_type.rs index 4942df4e..8a681900 100644 --- a/crates/webidl/src/idl_type.rs +++ b/crates/webidl/src/idl_type.rs @@ -1,12 +1,11 @@ use backend::util::{ident_ty, leading_colon_path_ty, raw_ident, rust_ident}; -use heck::SnakeCase; use syn; use weedle::common::Identifier; use weedle::term; use weedle::types::*; use first_pass::FirstPassRecord; -use util::{TypePosition, camel_case_ident, shared_ref, option_ty, array}; +use util::{TypePosition, camel_case_ident, snake_case_ident, shared_ref, option_ty, array}; #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)] pub(crate) enum IdlType<'a> { @@ -337,7 +336,7 @@ terms_to_idl_type! { impl<'a> IdlType<'a> { /// Generates a snake case type name. - pub(crate) fn push_type_name(&self, dst: &mut String) { + pub(crate) fn push_snake_case_name(&self, dst: &mut String) { match self { IdlType::Boolean => dst.push_str("bool"), IdlType::Byte => dst.push_str("i8"), @@ -348,13 +347,13 @@ impl<'a> IdlType<'a> { IdlType::UnsignedLong => dst.push_str("u32"), IdlType::LongLong => dst.push_str("i64"), IdlType::UnsignedLongLong => dst.push_str("u64"), - IdlType::Float | - IdlType::UnrestrictedFloat => dst.push_str("f32"), - IdlType::Double | - IdlType::UnrestrictedDouble => dst.push_str("f64"), - IdlType::DomString | - IdlType::ByteString | - IdlType::UsvString => dst.push_str("str"), + | IdlType::Float + | IdlType::UnrestrictedFloat => dst.push_str("f32"), + | IdlType::Double + | IdlType::UnrestrictedDouble => dst.push_str("f64"), + | IdlType::DomString + | IdlType::ByteString + | IdlType::UsvString => dst.push_str("str"), IdlType::Object => dst.push_str("object"), IdlType::Symbol => dst.push_str("symbol"), IdlType::Error => dst.push_str("error"), @@ -371,31 +370,31 @@ impl<'a> IdlType<'a> { IdlType::Float32Array => dst.push_str("f32_array"), IdlType::Float64Array => dst.push_str("f64_array"), - IdlType::Interface(name) => dst.push_str(&name.to_snake_case()), - IdlType::Dictionary(name) => dst.push_str(&name.to_snake_case()), - IdlType::Enum(name) => dst.push_str(&name.to_snake_case()), + IdlType::Interface(name) => dst.push_str(&snake_case_ident(name)), + IdlType::Dictionary(name) => dst.push_str(&snake_case_ident(name)), + IdlType::Enum(name) => dst.push_str(&snake_case_ident(name)), IdlType::Nullable(idl_type) => { dst.push_str("opt_"); - idl_type.push_type_name(dst); + idl_type.push_snake_case_name(dst); }, IdlType::FrozenArray(idl_type) => { - idl_type.push_type_name(dst); + idl_type.push_snake_case_name(dst); dst.push_str("_frozen_array"); }, IdlType::Sequence(idl_type) => { - idl_type.push_type_name(dst); + idl_type.push_snake_case_name(dst); dst.push_str("_sequence"); }, IdlType::Promise(idl_type) => { - idl_type.push_type_name(dst); + idl_type.push_snake_case_name(dst); dst.push_str("_promise"); }, IdlType::Record(idl_type_from, idl_type_to) => { dst.push_str("record_from_"); - idl_type_from.push_type_name(dst); + idl_type_from.push_snake_case_name(dst); dst.push_str("_to_"); - idl_type_to.push_type_name(dst); + idl_type_to.push_snake_case_name(dst); }, IdlType::Union(idl_types) => { dst.push_str("union_of_"); @@ -406,7 +405,7 @@ impl<'a> IdlType<'a> { } else { dst.push_str("_and_"); } - idl_type.push_type_name(dst); + idl_type.push_snake_case_name(dst); } }, @@ -415,14 +414,6 @@ impl<'a> IdlType<'a> { } } - /// Generates a snake case type name. - #[allow(dead_code)] - pub(crate) fn get_type_name(&self) -> String { - let mut string = String::new(); - self.push_type_name(&mut string); - return string; - } - /// Converts to syn type if possible. pub(crate) fn to_syn_type(&self, pos: TypePosition) -> Option { match self { @@ -467,8 +458,8 @@ impl<'a> IdlType<'a> { IdlType::Float32Array => Some(array("f32", pos)), IdlType::Float64Array => Some(array("f64", pos)), - IdlType::Interface(name) | - IdlType::Dictionary(name) => { + | IdlType::Interface(name) + | IdlType::Dictionary(name) => { let ty = ident_ty(rust_ident(camel_case_ident(name).as_str())); if pos == TypePosition::Argument { Some(shared_ref(ty)) @@ -491,7 +482,27 @@ impl<'a> IdlType<'a> { } } IdlType::Record(_idl_type_from, _idl_type_to) => None, - IdlType::Union(_idl_types) => None, + IdlType::Union(idl_types) => { + // Handles union types in all places except operation argument types. + // Currently treats them as object type, if possible. + // TODO: add better support for union types here? + // Approaches for it: + // 1. Use strategy of finding the nearest common subclass (finding the best type + // that is suitable for all values of this union) + // 2. Generate enum with payload in Rust for each union type + if idl_types + .iter() + .all(|idl_type| + match idl_type { + IdlType::Interface(..) => true, + _ => false, + } + ) { + IdlType::Object.to_syn_type(pos) + } else { + None + } + }, IdlType::Any => { let path = vec![rust_ident("wasm_bindgen"), rust_ident("JsValue")]; @@ -552,7 +563,7 @@ impl<'a> IdlType<'a> { .flat_map(|idl_type| idl_type.flatten()) .collect(), - idl_type @ _ => vec![idl_type.clone()] + idl_type @ _ => vec![idl_type.clone()], } } } diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index 5f0e2fc4..5023763e 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -41,14 +41,13 @@ use backend::defined::{ImportedTypeDefinitions, RemoveUndefinedImports}; use backend::defined::ImportedTypeReferences; use backend::util::{ident_ty, rust_ident, raw_ident, wrap_import_function}; use failure::ResultExt; -use heck::{ShoutySnakeCase, SnakeCase}; use proc_macro2::{Ident, Span}; use weedle::attribute::{ExtendedAttributeList}; use weedle::dictionary::DictionaryMember; use first_pass::{FirstPass, FirstPassRecord, OperationId, InterfaceData}; use first_pass::OperationData; -use util::{public, webidl_const_v_to_backend_const_v, TypePosition, camel_case_ident, mdn_doc}; +use util::{public, webidl_const_v_to_backend_const_v, TypePosition, camel_case_ident, shouty_snake_case_ident, snake_case_ident, mdn_doc}; use idl_type::ToIdlType; pub use error::{Error, ErrorKind, Result}; @@ -280,7 +279,7 @@ impl<'src> FirstPassRecord<'src> { Some(ast::DictionaryField { required: field.required.is_some(), - name: rust_ident(&field.identifier.0.to_snake_case()), + name: rust_ident(&snake_case_ident(field.identifier.0)), ty, }) } @@ -293,7 +292,7 @@ impl<'src> FirstPassRecord<'src> { ) { let mut module = backend::ast::Module { vis: public(), - name: rust_ident(name.to_snake_case().as_str()), + name: rust_ident(snake_case_ident(name).as_str()), imports: Default::default(), }; @@ -368,7 +367,7 @@ impl<'src> FirstPassRecord<'src> { program.consts.push(backend::ast::Const { vis: public(), - name: rust_ident(member.identifier.0.to_shouty_snake_case().as_str()), + name: rust_ident(shouty_snake_case_ident(member.identifier.0).as_str()), class: Some(rust_ident(camel_case_ident(&self_name).as_str())), ty, value: webidl_const_v_to_backend_const_v(&member.const_value), diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index b3463535..e583fcf3 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -3,7 +3,7 @@ use std::ptr; use backend; use backend::util::{ident_ty, leading_colon_path_ty, raw_ident, rust_ident}; -use heck::{CamelCase, SnakeCase}; +use heck::{CamelCase, ShoutySnakeCase, SnakeCase}; use proc_macro2::Ident; use syn; use weedle; @@ -23,9 +23,28 @@ pub(crate) fn shared_ref(ty: syn::Type) -> syn::Type { }.into() } -/// Fix camelcase of identifiers like HTMLBRElement +/// Fix case of identifiers like `HTMLBRElement` or `texImage2D` +fn fix_ident(identifier: &str) -> String { + identifier + .replace("HTML", "HTML_") + .replace("1D", "_1d") + .replace("2D", "_2d") + .replace("3D", "_3d") +} + +/// Convert an identifier to camel case pub fn camel_case_ident(identifier: &str) -> String { - identifier.replace("HTML", "HTML_").to_camel_case() + fix_ident(identifier).to_camel_case() +} + +/// Convert an identifier to shouty snake case +pub fn shouty_snake_case_ident(identifier: &str) -> String { + fix_ident(identifier).to_shouty_snake_case() +} + +/// Convert an identifier to snake case +pub fn snake_case_ident(identifier: &str) -> String { + fix_ident(identifier).to_snake_case() } // Returns a link to MDN @@ -309,7 +328,7 @@ impl<'src> FirstPassRecord<'src> { let ret = ty.to_idl_type(self)?; self.create_one_function( &name, - &name.to_snake_case(), + &snake_case_ident(name), None.into_iter(), &ret, kind, @@ -494,7 +513,7 @@ impl<'src> FirstPassRecord<'src> { None => continue, }; - let mut rust_name = name.to_snake_case(); + let mut rust_name = snake_case_ident(name); let mut first = true; for (i, arg) in signature.args.iter().enumerate() { // Find out if any other known signature either has the same @@ -541,9 +560,9 @@ impl<'src> FirstPassRecord<'src> { // then we can't use that if the types are also the same because // otherwise it could be ambiguous. if any_same_name && any_different_type { - arg.push_type_name(&mut rust_name); + arg.push_snake_case_name(&mut rust_name); } else { - rust_name.push_str(&arg_name.to_snake_case()); + rust_name.push_str(&snake_case_ident(arg_name)); } } ret.extend(self.create_one_function( diff --git a/examples/canvas/src/lib.rs b/examples/canvas/src/lib.rs index 420016e2..1b200c4b 100755 --- a/examples/canvas/src/lib.rs +++ b/examples/canvas/src/lib.rs @@ -19,7 +19,7 @@ pub fn draw() { .get_context("2d") .unwrap() .unwrap() - .dyn_into::() + .dyn_into::() .unwrap(); context.begin_path();