diff --git a/Cargo.toml b/Cargo.toml index a7ba97bd..b4d7ce1d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,11 +2,14 @@ members = [ "gl", "gl_generator", + "webgl_stdweb", + "webgl_generator", "tests/test_add_registries", "tests/test_gen_symbols", "tests/test_no_warnings", "tests/test_symbols", "tests/test_unstable_api", "tests/test_with_extensions", + "tests/test_webgl_stdweb", "khronos_api", ] diff --git a/gl/build.rs b/gl/build.rs index 7ebfef80..0d18a8a3 100644 --- a/gl/build.rs +++ b/gl/build.rs @@ -14,7 +14,7 @@ extern crate gl_generator; -use gl_generator::{Registry, Fallbacks, GlobalGenerator, Api, Profile}; +use gl_generator::{Api, Fallbacks, GlobalGenerator, Profile, Registry}; use std::env; use std::fs::File; use std::path::Path; diff --git a/gl_generator/generators/debug_struct_gen.rs b/gl_generator/generators/debug_struct_gen.rs index 07200481..1cd4af5c 100644 --- a/gl_generator/generators/debug_struct_gen.rs +++ b/gl_generator/generators/debug_struct_gen.rs @@ -20,7 +20,8 @@ pub struct DebugStructGenerator; impl super::Generator for DebugStructGenerator { fn write(&self, registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write + where + W: io::Write, { try!(write_header(dest)); try!(write_type_aliases(registry, dest)); @@ -36,29 +37,35 @@ impl super::Generator for DebugStructGenerator { /// Creates a `__gl_imports` module which contains all the external symbols that we need for the /// bindings. fn write_header(dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { - writeln!(dest, - r#" + writeln!( + dest, + r#" mod __gl_imports {{ pub use std::mem; pub use std::marker::Send; pub use std::os::raw; }} - "#) + "# + ) } /// Creates a `types` module which contains all the type aliases. /// /// See also `generators::gen_types`. fn write_type_aliases(registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { - try!(writeln!(dest, - r#" + try!(writeln!( + dest, + r#" pub mod types {{ #![allow(non_camel_case_types, non_snake_case, dead_code, missing_copy_implementations)] - "#)); + "# + )); try!(super::gen_types(registry.api, dest)); @@ -67,7 +74,8 @@ fn write_type_aliases(registry: &Registry, dest: &mut W) -> io::Result<()> /// Creates all the `` elements at the root of the bindings. fn write_enums(registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { for enm in ®istry.enums { try!(super::gen_enum_item(enm, "types::", dest)); @@ -78,10 +86,12 @@ fn write_enums(registry: &Registry, dest: &mut W) -> io::Result<()> /// Creates a `FnPtr` structure which contains the store for a single binding. fn write_fnptr_struct_def(dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { - writeln!(dest, - " + writeln!( + dest, + " #[allow(dead_code, missing_copy_implementations)] #[derive(Clone)] pub struct FnPtr {{ @@ -113,35 +123,42 @@ fn write_fnptr_struct_def(dest: &mut W) -> io::Result<()> self.is_loaded }} }} - ") + " + ) } /// Creates a `panicking` module which contains one function per GL command. /// /// These functions are the mocks that are called if the real function could not be loaded. fn write_panicking_fns(registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { - writeln!(dest, - "#[inline(never)] + writeln!( + dest, + "#[inline(never)] fn missing_fn_panic() -> ! {{ panic!(\"{api} function was not loaded\") }}", - api = registry.api) + api = registry.api + ) } /// Creates a structure which stores all the `FnPtr` of the bindings. /// /// The name of the struct corresponds to the namespace. fn write_struct(registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { - try!(writeln!(dest, - " + try!(writeln!( + dest, + " #[allow(non_camel_case_types, non_snake_case, dead_code)] #[derive(Clone)] pub struct {api} {{", - api = super::gen_struct_name(registry.api))); + api = super::gen_struct_name(registry.api) + )); for cmd in ®istry.cmds { if let Some(v) = registry.aliases.get(&cmd.proto.ident) { @@ -156,7 +173,8 @@ fn write_struct(registry: &Registry, dest: &mut W) -> io::Result<()> /// Creates the `impl` of the structure created by `write_struct`. fn write_impl(registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { try!(writeln!(dest, "impl {api} {{ @@ -189,45 +207,49 @@ fn write_impl(registry: &Registry, dest: &mut W) -> io::Result<()> api = super::gen_struct_name(registry.api))); for cmd in ®istry.cmds { - try!(writeln!(dest, + try!(writeln!( + dest, "{name}: FnPtr::new(metaloadfn(\"{symbol}\", &[{fallbacks}])),", name = cmd.proto.ident, symbol = super::gen_symbol_name(registry.api, &cmd.proto.ident), fallbacks = match registry.aliases.get(&cmd.proto.ident) { - Some(fbs) => { - fbs.iter() - .map(|name| format!("\"{}\"", super::gen_symbol_name(registry.api, &name))) - .collect::>().join(", ") - }, + Some(fbs) => fbs.iter() + .map(|name| format!("\"{}\"", super::gen_symbol_name(registry.api, &name))) + .collect::>() + .join(", "), None => format!(""), }, )) } try!(writeln!(dest, "_priv: ()")); - try!(writeln!(dest, - "}} - }}")); + try!(writeln!( + dest, + "}} + }}" + )); for cmd in ®istry.cmds { let idents = super::gen_parameters(cmd, true, false); let typed_params = super::gen_parameters(cmd, false, true); - let println = format!("println!(\"[OpenGL] {}({})\" {});", - cmd.proto.ident, - (0..idents.len()) - .map(|_| "{:?}".to_string()) - .collect::>() - .join(", "), - idents - .iter() - .zip(typed_params.iter()) - .map(|(name, ty)| if ty.contains("GLDEBUGPROC") { - format!(", \"\"") - } else { - format!(", {}", name) - }) - .collect::>() - .concat()); + let println = format!( + "println!(\"[OpenGL] {}({})\" {});", + cmd.proto.ident, + (0..idents.len()) + .map(|_| "{:?}".to_string()) + .collect::>() + .join(", "), + idents + .iter() + .zip(typed_params.iter()) + .map(|(name, ty)| if ty.contains("GLDEBUGPROC") { + format!(", \"\"") + } else { + format!(", {}", name) + }) + .collect::>() + .concat() + ); try!(writeln!(dest, "#[allow(non_snake_case, unused_variables, dead_code)] @@ -257,9 +279,11 @@ fn write_impl(registry: &Registry, dest: &mut W) -> io::Result<()> })) } - writeln!(dest, - "}} + writeln!( + dest, + "}} unsafe impl __gl_imports::Send for {api} {{}}", - api = super::gen_struct_name(registry.api)) + api = super::gen_struct_name(registry.api) + ) } diff --git a/gl_generator/generators/global_gen.rs b/gl_generator/generators/global_gen.rs index cb1cf2c2..11dffc90 100644 --- a/gl_generator/generators/global_gen.rs +++ b/gl_generator/generators/global_gen.rs @@ -20,7 +20,8 @@ pub struct GlobalGenerator; impl super::Generator for GlobalGenerator { fn write(&self, registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write + where + W: io::Write, { try!(write_header(dest)); try!(write_metaloadfn(dest)); @@ -39,23 +40,28 @@ impl super::Generator for GlobalGenerator { /// Creates a `__gl_imports` module which contains all the external symbols that we need for the /// bindings. fn write_header(dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { - writeln!(dest, - r#" + writeln!( + dest, + r#" mod __gl_imports {{ pub use std::mem; pub use std::os::raw; }} - "#) + "# + ) } /// Creates the metaloadfn function for fallbacks fn write_metaloadfn(dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { - writeln!(dest, - r#" + writeln!( + dest, + r#" #[inline(never)] fn metaloadfn(loadfn: &mut FnMut(&'static str) -> *const __gl_imports::raw::c_void, symbol: &'static str, @@ -69,32 +75,39 @@ fn write_metaloadfn(dest: &mut W) -> io::Result<()> }} ptr }} - "#) + "# + ) } /// Creates a `types` module which contains all the type aliases. /// /// See also `generators::gen_types`. fn write_type_aliases(registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { - try!(writeln!(dest, - r#" + try!(writeln!( + dest, + r#" pub mod types {{ #![allow(non_camel_case_types, non_snake_case, dead_code, missing_copy_implementations)] - "#)); + "# + )); try!(super::gen_types(registry.api, dest)); - writeln!(dest, - " + writeln!( + dest, + " }} - ") + " + ) } /// Creates all the `` elements at the root of the bindings. fn write_enums(registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { for enm in ®istry.enums { try!(super::gen_enum_item(enm, "types::", dest)); @@ -108,7 +121,8 @@ fn write_enums(registry: &Registry, dest: &mut W) -> io::Result<()> /// The function calls the corresponding function pointer stored in the `storage` module created /// by `write_ptrs`. fn write_fns(registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { for cmd in ®istry.cmds { if let Some(v) = registry.aliases.get(&cmd.proto.ident) { @@ -134,7 +148,8 @@ fn write_fns(registry: &Registry, dest: &mut W) -> io::Result<()> /// Creates a `FnPtr` structure which contains the store for a single binding. fn write_fnptr_struct_def(dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { writeln!(dest, " @@ -161,23 +176,27 @@ fn write_fnptr_struct_def(dest: &mut W) -> io::Result<()> /// Creates a `storage` module which contains a static `FnPtr` per GL command in the registry. fn write_ptrs(registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { - - try!(writeln!(dest, - "mod storage {{ + try!(writeln!( + dest, + "mod storage {{ #![allow(non_snake_case)] #![allow(non_upper_case_globals)] use super::__gl_imports::raw; - use super::FnPtr;")); + use super::FnPtr;" + )); for c in ®istry.cmds { - try!(writeln!(dest, - "pub static mut {name}: FnPtr = FnPtr {{ + try!(writeln!( + dest, + "pub static mut {name}: FnPtr = FnPtr {{ f: super::missing_fn_panic as *const raw::c_void, is_loaded: false }};", - name = c.proto.ident)); + name = c.proto.ident + )); } writeln!(dest, "}}") @@ -188,7 +207,8 @@ fn write_ptrs(registry: &Registry, dest: &mut W) -> io::Result<()> /// Each module contains `is_loaded` and `load_with` which interact with the `storage` module /// created by `write_ptrs`. fn write_fn_mods(registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { for c in ®istry.cmds { let fallbacks = match registry.aliases.get(&c.proto.ident) { @@ -234,22 +254,26 @@ fn write_fn_mods(registry: &Registry, dest: &mut W) -> io::Result<()> /// /// This function is the mock that is called if the real function could not be called. fn write_panicking_fns(registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { - writeln!(dest, - "#[inline(never)] + writeln!( + dest, + "#[inline(never)] fn missing_fn_panic() -> ! {{ panic!(\"{api} function was not loaded\") }} ", - api = registry.api) + api = registry.api + ) } /// Creates the `load_with` function. /// /// The function calls `load_with` in each module created by `write_fn_mods`. fn write_load_fn(registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { try!(writeln!(dest, " @@ -263,13 +287,17 @@ fn write_load_fn(registry: &Registry, dest: &mut W) -> io::Result<()> ")); for c in ®istry.cmds { - try!(writeln!(dest, - "{cmd_name}::load_with(&mut loadfn);", - cmd_name = &c.proto.ident[..])); + try!(writeln!( + dest, + "{cmd_name}::load_with(&mut loadfn);", + cmd_name = &c.proto.ident[..] + )); } - writeln!(dest, - " + writeln!( + dest, + " }} - ") + " + ) } diff --git a/gl_generator/generators/mod.rs b/gl_generator/generators/mod.rs index 83d346e6..28c6cce6 100644 --- a/gl_generator/generators/mod.rs +++ b/gl_generator/generators/mod.rs @@ -13,7 +13,7 @@ // limitations under the License. use Api; -use registry::{Enum, Registry, Cmd}; +use registry::{Cmd, Enum, Registry}; use std::io; pub mod debug_struct_gen; @@ -25,7 +25,9 @@ pub mod static_struct_gen; /// Trait for a bindings generator. pub trait Generator { /// Builds the GL bindings. - fn write(&self, registry: &Registry, dest: &mut W) -> io::Result<()> where W: io::Write; + fn write(&self, registry: &Registry, dest: &mut W) -> io::Result<()> + where + W: io::Write; } pub fn gen_struct_name(api: Api) -> &'static str { @@ -43,7 +45,8 @@ pub fn gen_struct_name(api: Api) -> &'static str { /// This function generates a `const name: type = value;` item. pub fn gen_enum_item(enm: &Enum, types_prefix: &str, dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { writeln!(dest, "#[allow(dead_code, non_upper_case_globals)] pub const {ident}: {types_prefix}{ty} = {value}{cast_suffix};", @@ -63,7 +66,8 @@ pub fn gen_enum_item(enm: &Enum, types_prefix: &str, dest: &mut W) -> io::Res /// Aliases are either `pub type = ...` or `#[repr(C)] pub struct ... { ... }` and contain all the /// things that we can't obtain from the XML files. pub fn gen_types(api: Api, dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { if let Api::Egl = api { try!(writeln!(dest, "{}", include_str!("templates/types/egl.rs"))); diff --git a/gl_generator/generators/static_gen.rs b/gl_generator/generators/static_gen.rs index cbab248b..8d15de01 100644 --- a/gl_generator/generators/static_gen.rs +++ b/gl_generator/generators/static_gen.rs @@ -20,7 +20,8 @@ pub struct StaticGenerator; impl super::Generator for StaticGenerator { fn write(&self, registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write + where + W: io::Write, { try!(write_header(dest)); try!(write_type_aliases(registry, dest)); @@ -33,40 +34,49 @@ impl super::Generator for StaticGenerator { /// Creates a `__gl_imports` module which contains all the external symbols that we need for the /// bindings. fn write_header(dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { - writeln!(dest, - r#" + writeln!( + dest, + r#" mod __gl_imports {{ pub use std::mem; pub use std::os::raw; }} - "#) + "# + ) } /// Creates a `types` module which contains all the type aliases. /// /// See also `generators::gen_types`. fn write_type_aliases(registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { - try!(writeln!(dest, - r#" + try!(writeln!( + dest, + r#" pub mod types {{ #![allow(non_camel_case_types, non_snake_case, dead_code, missing_copy_implementations)] - "#)); + "# + )); try!(super::gen_types(registry.api, dest)); - writeln!(dest, - " + writeln!( + dest, + " }} - ") + " + ) } /// Creates all the `` elements at the root of the bindings. fn write_enums(registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { for enm in ®istry.enums { try!(super::gen_enum_item(enm, "types::", dest)); @@ -79,15 +89,19 @@ fn write_enums(registry: &Registry, dest: &mut W) -> io::Result<()> /// /// These are foreign functions, they don't have any content. fn write_fns(registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { - try!(writeln!(dest, - " + try!(writeln!( + dest, + " #[allow(non_snake_case, unused_variables, dead_code)] - extern \"system\" {{")); + extern \"system\" {{" + )); for cmd in ®istry.cmds { - try!(writeln!(dest, + try!(writeln!( + dest, "#[link_name=\"{symbol}\"] pub fn {name}({params}) -> {return_suffix};", symbol = super::gen_symbol_name(registry.api, &cmd.proto.ident), diff --git a/gl_generator/generators/static_struct_gen.rs b/gl_generator/generators/static_struct_gen.rs index 473162a6..c03a3e09 100644 --- a/gl_generator/generators/static_struct_gen.rs +++ b/gl_generator/generators/static_struct_gen.rs @@ -20,7 +20,8 @@ pub struct StaticStructGenerator; impl super::Generator for StaticStructGenerator { fn write(&self, registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write + where + W: io::Write, { try!(write_header(dest)); try!(write_type_aliases(registry, dest)); @@ -35,28 +36,34 @@ impl super::Generator for StaticStructGenerator { /// Creates a `__gl_imports` module which contains all the external symbols that we need for the /// bindings. fn write_header(dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { - writeln!(dest, - r#" + writeln!( + dest, + r#" mod __gl_imports {{ pub use std::mem; pub use std::os::raw; }} - "#) + "# + ) } /// Creates a `types` module which contains all the type aliases. /// /// See also `generators::gen_types`. fn write_type_aliases(registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { - try!(writeln!(dest, - r#" + try!(writeln!( + dest, + r#" pub mod types {{ #![allow(non_camel_case_types, non_snake_case, dead_code, missing_copy_implementations)] - "#)); + "# + )); try!(super::gen_types(registry.api, dest)); @@ -65,7 +72,8 @@ fn write_type_aliases(registry: &Registry, dest: &mut W) -> io::Result<()> /// Creates all the `` elements at the root of the bindings. fn write_enums(registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { for enm in ®istry.enums { try!(super::gen_enum_item(enm, "types::", dest)); @@ -78,9 +86,12 @@ fn write_enums(registry: &Registry, dest: &mut W) -> io::Result<()> /// /// The name of the struct corresponds to the namespace. fn write_struct(registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { - writeln!(dest, " + writeln!( + dest, + " #[allow(non_camel_case_types, non_snake_case, dead_code)] #[derive(Copy, Clone)] pub struct {api};", @@ -90,7 +101,8 @@ fn write_struct(registry: &Registry, dest: &mut W) -> io::Result<()> /// Creates the `impl` of the structure created by `write_struct`. fn write_impl(registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { try!(writeln!(dest, "impl {api} {{ @@ -103,7 +115,8 @@ fn write_impl(registry: &Registry, dest: &mut W) -> io::Result<()> )); for cmd in ®istry.cmds { - try!(writeln!(dest, + try!(writeln!( + dest, "#[allow(non_snake_case)] // #[allow(unused_variables)] #[allow(dead_code)] @@ -125,18 +138,21 @@ fn write_impl(registry: &Registry, dest: &mut W) -> io::Result<()> /// /// These are foreign functions, they don't have any content. fn write_fns(registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { - - try!(writeln!(dest, - " + try!(writeln!( + dest, + " #[allow(non_snake_case)] #[allow(unused_variables)] #[allow(dead_code)] - extern \"system\" {{")); + extern \"system\" {{" + )); for cmd in ®istry.cmds { - try!(writeln!(dest, + try!(writeln!( + dest, "#[link_name=\"{symbol}\"] fn {name}({params}) -> {return_suffix};", symbol = super::gen_symbol_name(registry.api, &cmd.proto.ident), name = cmd.proto.ident, diff --git a/gl_generator/generators/struct_gen.rs b/gl_generator/generators/struct_gen.rs index c74dd095..c6adfdea 100644 --- a/gl_generator/generators/struct_gen.rs +++ b/gl_generator/generators/struct_gen.rs @@ -20,7 +20,8 @@ pub struct StructGenerator; impl super::Generator for StructGenerator { fn write(&self, registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write + where + W: io::Write, { try!(write_header(dest)); try!(write_type_aliases(registry, dest)); @@ -36,29 +37,35 @@ impl super::Generator for StructGenerator { /// Creates a `__gl_imports` module which contains all the external symbols that we need for the /// bindings. fn write_header(dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { - writeln!(dest, - r#" + writeln!( + dest, + r#" mod __gl_imports {{ pub use std::mem; pub use std::marker::Send; pub use std::os::raw; }} - "#) + "# + ) } /// Creates a `types` module which contains all the type aliases. /// /// See also `generators::gen_types`. fn write_type_aliases(registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { - try!(writeln!(dest, - r#" + try!(writeln!( + dest, + r#" pub mod types {{ #![allow(non_camel_case_types, non_snake_case, dead_code, missing_copy_implementations)] - "#)); + "# + )); try!(super::gen_types(registry.api, dest)); @@ -67,7 +74,8 @@ fn write_type_aliases(registry: &Registry, dest: &mut W) -> io::Result<()> /// Creates all the `` elements at the root of the bindings. fn write_enums(registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { for enm in ®istry.enums { try!(super::gen_enum_item(enm, "types::", dest)); @@ -78,10 +86,12 @@ fn write_enums(registry: &Registry, dest: &mut W) -> io::Result<()> /// Creates a `FnPtr` structure which contains the store for a single binding. fn write_fnptr_struct_def(dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { - writeln!(dest, - " + writeln!( + dest, + " #[allow(dead_code, missing_copy_implementations)] #[derive(Clone)] pub struct FnPtr {{ @@ -113,35 +123,42 @@ fn write_fnptr_struct_def(dest: &mut W) -> io::Result<()> self.is_loaded }} }} - ") + " + ) } /// Creates a `panicking` module which contains one function per GL command. /// /// These functions are the mocks that are called if the real function could not be loaded. fn write_panicking_fns(registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { - writeln!(dest, - "#[inline(never)] + writeln!( + dest, + "#[inline(never)] fn missing_fn_panic() -> ! {{ panic!(\"{api} function was not loaded\") }}", - api = registry.api) + api = registry.api + ) } /// Creates a structure which stores all the `FnPtr` of the bindings. /// /// The name of the struct corresponds to the namespace. fn write_struct(registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { - try!(writeln!(dest, - " + try!(writeln!( + dest, + " #[allow(non_camel_case_types, non_snake_case, dead_code)] #[derive(Clone)] pub struct {api} {{", - api = super::gen_struct_name(registry.api))); + api = super::gen_struct_name(registry.api) + )); for cmd in ®istry.cmds { if let Some(v) = registry.aliases.get(&cmd.proto.ident) { @@ -156,7 +173,8 @@ fn write_struct(registry: &Registry, dest: &mut W) -> io::Result<()> /// Creates the `impl` of the structure created by `write_struct`. fn write_impl(registry: &Registry, dest: &mut W) -> io::Result<()> - where W: io::Write +where + W: io::Write, { try!(writeln!(dest, "impl {api} {{ @@ -189,16 +207,16 @@ fn write_impl(registry: &Registry, dest: &mut W) -> io::Result<()> api = super::gen_struct_name(registry.api))); for cmd in ®istry.cmds { - try!(writeln!(dest, + try!(writeln!( + dest, "{name}: FnPtr::new(metaloadfn(\"{symbol}\", &[{fallbacks}])),", name = cmd.proto.ident, symbol = super::gen_symbol_name(registry.api, &cmd.proto.ident), fallbacks = match registry.aliases.get(&cmd.proto.ident) { - Some(fbs) => { - fbs.iter() - .map(|name| format!("\"{}\"", super::gen_symbol_name(registry.api, &name))) - .collect::>().join(", ") - }, + Some(fbs) => fbs.iter() + .map(|name| format!("\"{}\"", super::gen_symbol_name(registry.api, &name))) + .collect::>() + .join(", "), None => format!(""), }, )) @@ -206,9 +224,11 @@ fn write_impl(registry: &Registry, dest: &mut W) -> io::Result<()> try!(writeln!(dest, "_priv: ()")); - try!(writeln!(dest, - "}} - }}")); + try!(writeln!( + dest, + "}} + }}" + )); for cmd in ®istry.cmds { try!(writeln!(dest, @@ -225,9 +245,11 @@ fn write_impl(registry: &Registry, dest: &mut W) -> io::Result<()> )) } - writeln!(dest, - "}} + writeln!( + dest, + "}} unsafe impl __gl_imports::Send for {api} {{}}", - api = super::gen_struct_name(registry.api)) + api = super::gen_struct_name(registry.api) + ) } diff --git a/gl_generator/registry/mod.rs b/gl_generator/registry/mod.rs index 73642aed..c7cf6ee4 100644 --- a/gl_generator/registry/mod.rs +++ b/gl_generator/registry/mod.rs @@ -115,20 +115,18 @@ pub struct Registry { } impl Registry { - pub fn new<'a, Exts>(api: Api, - version: (u8, u8), - profile: Profile, - fallbacks: Fallbacks, - extensions: Exts) - -> Registry - where Exts: AsRef<[&'a str]> + pub fn new<'a, Exts>( + api: Api, + version: (u8, u8), + profile: Profile, + fallbacks: Fallbacks, + extensions: Exts, + ) -> Registry + where + Exts: AsRef<[&'a str]>, { let (major, minor) = version; - let extensions = extensions - .as_ref() - .iter() - .map(<&str>::to_string) - .collect(); + let extensions = extensions.as_ref().iter().map(<&str>::to_string).collect(); let filter = parse::Filter { api: api, @@ -149,8 +147,9 @@ impl Registry { } pub fn write_bindings(&self, generator: G, output: &mut W) -> io::Result<()> - where G: Generator, - W: io::Write + where + G: Generator, + W: io::Write, { generator.write(&self, output) } diff --git a/gl_generator/registry/parse.rs b/gl_generator/registry/parse.rs index 0e8a0ca3..b41daa45 100644 --- a/gl_generator/registry/parse.rs +++ b/gl_generator/registry/parse.rs @@ -22,7 +22,7 @@ use xml::attribute::OwnedAttribute; use xml::EventReader as XmlEventReader; use xml::reader::XmlEvent; -use {Fallbacks, Api, Profile}; +use {Api, Fallbacks, Profile}; use registry::{Binding, Cmd, Enum, GlxOpcode, Registry}; pub fn from_xml(src: R, filter: Filter) -> Registry { @@ -41,8 +41,9 @@ struct Attribute { impl Attribute { fn new(key: Key, value: Value) -> Attribute - where Key: ToString, - Value: ToString + where + Key: ToString, + Value: ToString, { Attribute { key: key.to_string(), @@ -69,7 +70,9 @@ impl ParseEvent { match event { XmlEvent::StartDocument { .. } => None, XmlEvent::EndDocument => None, - XmlEvent::StartElement { name, attributes, .. } => { + XmlEvent::StartElement { + name, attributes, .. + } => { let attributes = attributes.into_iter().map(Attribute::from).collect(); Some(ParseEvent::Start(name.local_name, attributes)) } @@ -193,7 +196,7 @@ fn make_egl_enum(ident: String, ty: Option, value: String, alias: Option } } else { match value.chars().next() { - Some('-') | Some('0' ... '9') => (), + Some('-') | Some('0'...'9') => (), _ => panic!("Unexpected value format: {}", value), } @@ -217,7 +220,6 @@ fn make_egl_enum(ident: String, ty: Option, value: String, alias: Option } } - fn trim_cmd_prefix(ident: &str, api: Api) -> &str { match api { Api::Gl | Api::GlCore | Api::Gles1 | Api::Gles2 | Api::Glsc2 => trim_str(ident, "gl"), @@ -318,17 +320,15 @@ trait Parse: Sized + Iterator { features.push(Feature::convert(&mut self, &attributes)); } - ParseEvent::Start(ref name, _) if name == "extensions" => { - loop { - match self.next().unwrap() { - ParseEvent::Start(ref name, ref attributes) if name == "extension" => { - extensions.push(Extension::convert(&mut self, &attributes)); - } - ParseEvent::End(ref name) if name == "extensions" => break, - event => panic!("Unexpected message {:?}", event), + ParseEvent::Start(ref name, _) if name == "extensions" => loop { + match self.next().unwrap() { + ParseEvent::Start(ref name, ref attributes) if name == "extension" => { + extensions.push(Extension::convert(&mut self, &attributes)); } + ParseEvent::End(ref name) if name == "extensions" => break, + event => panic!("Unexpected message {:?}", event), } - } + }, // finished building the registry ParseEvent::End(ref name) if name == "registry" => break, @@ -376,9 +376,10 @@ trait Parse: Sized + Iterator { for extension in &extensions { if filter.extensions.contains(&extension.name) { if !extension.supported.contains(&filter.api) { - panic!("Requested {}, which doesn't support the {} API", - extension.name, - filter.api); + panic!( + "Requested {}, which doesn't support the {} API", + extension.name, filter.api + ); } for require in &extension.requires { desired_enums.extend(require.enums.iter().map(|x| x.clone())); @@ -388,17 +389,17 @@ trait Parse: Sized + Iterator { } let is_desired_enum = |e: &Enum| { - desired_enums.contains(&("GL_".to_string() + &e.ident)) || - desired_enums.contains(&("WGL_".to_string() + &e.ident)) || - desired_enums.contains(&("GLX_".to_string() + &e.ident)) || - desired_enums.contains(&("EGL_".to_string() + &e.ident)) + desired_enums.contains(&("GL_".to_string() + &e.ident)) + || desired_enums.contains(&("WGL_".to_string() + &e.ident)) + || desired_enums.contains(&("GLX_".to_string() + &e.ident)) + || desired_enums.contains(&("EGL_".to_string() + &e.ident)) }; let is_desired_cmd = |c: &Cmd| { - desired_cmds.contains(&("gl".to_string() + &c.proto.ident)) || - desired_cmds.contains(&("wgl".to_string() + &c.proto.ident)) || - desired_cmds.contains(&("glX".to_string() + &c.proto.ident)) || - desired_cmds.contains(&("egl".to_string() + &c.proto.ident)) + desired_cmds.contains(&("gl".to_string() + &c.proto.ident)) + || desired_cmds.contains(&("wgl".to_string() + &c.proto.ident)) + || desired_cmds.contains(&("glX".to_string() + &c.proto.ident)) + || desired_cmds.contains(&("egl".to_string() + &c.proto.ident)) }; Registry { @@ -449,11 +450,12 @@ trait Parse: Sized + Iterator { } } - fn consume_two<'a, T: FromXml, U: FromXml>(&mut self, - one: &'a str, - two: &'a str, - end: &'a str) - -> (Vec, Vec) { + fn consume_two<'a, T: FromXml, U: FromXml>( + &mut self, + one: &'a str, + two: &'a str, + end: &'a str, + ) -> (Vec, Vec) { debug!("consume_two: looking for {} and {} until {}", one, two, end); let mut ones = Vec::new(); @@ -534,7 +536,7 @@ trait Parse: Sized + Iterator { match api { Api::Egl => make_egl_enum(ident, ty, value, alias), - _ => make_enum(ident, ty, value, alias) + _ => make_enum(ident, ty, value, alias), } } @@ -593,9 +595,9 @@ trait Parse: Sized + Iterator { } ParseEvent::Start(ref name, ref attributes) if name == "glx" => { glx = Some(GlxOpcode { - opcode: get_attribute(&attributes, "opcode").unwrap(), - name: get_attribute(&attributes, "name"), - }); + opcode: get_attribute(&attributes, "opcode").unwrap(), + name: get_attribute(&attributes, "name"), + }); self.consume_end_element("glx"); } ParseEvent::End(ref name) if name == "command" => break, @@ -645,7 +647,11 @@ trait Parse: Sized + Iterator { } } -impl Parse for T where T: Sized + Iterator {} +impl Parse for T +where + T: Sized + Iterator, +{ +} fn get_attribute(attribs: &[Attribute], key: &str) -> Option { attribs @@ -988,7 +994,9 @@ pub fn to_rust_ty>(ty: T) -> Cow<'static, str> { "EGLDisplay" => "types::EGLDisplay", "EGLSurface" => "types::EGLSurface", "EGLClientBuffer" => "types::EGLClientBuffer", - "__eglMustCastToProperFunctionPointerType" => "types::__eglMustCastToProperFunctionPointerType", + "__eglMustCastToProperFunctionPointerType" => { + "types::__eglMustCastToProperFunctionPointerType" + } "EGLImageKHR" => "types::EGLImageKHR", "EGLImage" => "types::EGLImage", "EGLOutputLayerEXT" => "types::EGLOutputLayerEXT", @@ -1076,10 +1084,12 @@ mod tests { #[test] fn test_cast_0() { - let e = parse::make_enum("FOO".to_string(), - None, - "((EGLint)-1)".to_string(), - Some("BAR".to_string())); + let e = parse::make_enum( + "FOO".to_string(), + None, + "((EGLint)-1)".to_string(), + Some("BAR".to_string()), + ); assert_eq!(e.ident, "FOO"); assert_eq!((&*e.ty, &*e.value), ("EGLint", "-1")); assert_eq!(e.alias, Some("BAR".to_string())); @@ -1087,10 +1097,12 @@ mod tests { #[test] fn test_cast_1() { - let e = parse::make_enum("FOO".to_string(), - None, - "((EGLint)(-1))".to_string(), - Some("BAR".to_string())); + let e = parse::make_enum( + "FOO".to_string(), + None, + "((EGLint)(-1))".to_string(), + Some("BAR".to_string()), + ); assert_eq!(e.ident, "FOO"); assert_eq!((&*e.ty, &*e.value), ("EGLint", "(-1)")); assert_eq!(e.alias, Some("BAR".to_string())); @@ -1098,10 +1110,12 @@ mod tests { #[test] fn test_no_type() { - let e = parse::make_enum("FOO".to_string(), - None, - "value".to_string(), - Some("BAR".to_string())); + let e = parse::make_enum( + "FOO".to_string(), + None, + "value".to_string(), + Some("BAR".to_string()), + ); assert_eq!(e.ident, "FOO"); assert_eq!(e.value, "value"); assert_eq!(e.alias, Some("BAR".to_string())); @@ -1111,29 +1125,35 @@ mod tests { #[test] fn test_u() { - let e = parse::make_enum("FOO".to_string(), - Some("u".to_string()), - String::new(), - None); + let e = parse::make_enum( + "FOO".to_string(), + Some("u".to_string()), + String::new(), + None, + ); assert_eq!(e.ty, "GLuint"); } #[test] fn test_ull() { - let e = parse::make_enum("FOO".to_string(), - Some("ull".to_string()), - String::new(), - None); + let e = parse::make_enum( + "FOO".to_string(), + Some("ull".to_string()), + String::new(), + None, + ); assert_eq!(e.ty, "GLuint64"); } #[test] #[should_panic] fn test_unknown_type() { - parse::make_enum("FOO".to_string(), - Some("blargh".to_string()), - String::new(), - None); + parse::make_enum( + "FOO".to_string(), + Some("blargh".to_string()), + String::new(), + None, + ); } #[test] @@ -1160,10 +1180,12 @@ mod tests { #[test] fn test_cast_egl() { - let e = parse::make_egl_enum("FOO".to_string(), - None, - "EGL_CAST(EGLint,-1)".to_string(), - Some("BAR".to_string())); + let e = parse::make_egl_enum( + "FOO".to_string(), + None, + "EGL_CAST(EGLint,-1)".to_string(), + Some("BAR".to_string()), + ); assert_eq!(e.ident, "FOO"); assert_eq!((&*e.ty, &*e.value), ("EGLint", "-1")); assert_eq!(e.alias, Some("BAR".to_string())); @@ -1183,47 +1205,42 @@ mod tests { #[test] fn test_ull() { - let e = parse::make_egl_enum("FOO".to_string(), - Some("ull".to_string()), - "1234".to_string(), - None); + let e = parse::make_egl_enum( + "FOO".to_string(), + Some("ull".to_string()), + "1234".to_string(), + None, + ); assert_eq!(e.ty, "EGLuint64KHR"); } #[test] fn test_negative_value() { - let e = parse::make_egl_enum("FOO".to_string(), - None, - "-1".to_string(), - None); + let e = parse::make_egl_enum("FOO".to_string(), None, "-1".to_string(), None); assert_eq!(e.ty, "EGLint"); } #[test] #[should_panic] fn test_unknown_type() { - parse::make_egl_enum("FOO".to_string(), - Some("blargh".to_string()), - String::new(), - None); + parse::make_egl_enum( + "FOO".to_string(), + Some("blargh".to_string()), + String::new(), + None, + ); } #[test] #[should_panic] fn test_unknown_value() { - parse::make_egl_enum("FOO".to_string(), - None, - "a".to_string(), - None); + parse::make_egl_enum("FOO".to_string(), None, "a".to_string(), None); } #[test] #[should_panic] fn test_empty_value() { - parse::make_egl_enum("FOO".to_string(), - None, - String::new(), - None); + parse::make_egl_enum("FOO".to_string(), None, String::new(), None); } } @@ -1241,19 +1258,27 @@ mod tests { fn test_start_event() { let given = XmlEvent::StartElement { name: OwnedName::local("element"), - attributes: vec![OwnedAttribute::new(OwnedName::local("attr1"), "val1"), - OwnedAttribute::new(OwnedName::local("attr2"), "val2")], + attributes: vec![ + OwnedAttribute::new(OwnedName::local("attr1"), "val1"), + OwnedAttribute::new(OwnedName::local("attr2"), "val2"), + ], namespace: Namespace::empty(), }; - let expected = ParseEvent::Start("element".to_string(), - vec![Attribute::new("attr1", "val1"), - Attribute::new("attr2", "val2")]); + let expected = ParseEvent::Start( + "element".to_string(), + vec![ + Attribute::new("attr1", "val1"), + Attribute::new("attr2", "val2"), + ], + ); assert_eq!(ParseEvent::from_xml(given), Some(expected)); } #[test] fn test_end_element() { - let given = XmlEvent::EndElement { name: OwnedName::local("element") }; + let given = XmlEvent::EndElement { + name: OwnedName::local("element"), + }; let expected = ParseEvent::End("element".to_string()); assert_eq!(ParseEvent::from_xml(given), Some(expected)); } diff --git a/khronos_api/src/lib.rs b/khronos_api/src/lib.rs index 02948d2e..caa432cc 100644 --- a/khronos_api/src/lib.rs +++ b/khronos_api/src/lib.rs @@ -36,4 +36,6 @@ pub const WEBGL_IDL: &'static [u8] = include_bytes!("../api_webgl/specs/latest/1 pub const WEBGL2_IDL: &'static [u8] = include_bytes!("../api_webgl/specs/latest/2.0/webgl2.idl"); /// The contents of the WebGL extension XML files -pub const WEBGL_EXT_XML: &'static [&'static [u8]] = include!(concat!(env!("OUT_DIR"), "/webgl_exts.rs")); +/// These are discovered via a build script to avoid having to list each extension by name. +pub const WEBGL_EXT_XML: &'static [&'static [u8]] = + include!(concat!(env!("OUT_DIR"), "/webgl_exts.rs")); diff --git a/tests/test_gen_symbols/lib.rs b/tests/test_gen_symbols/lib.rs index 89270c37..34566f86 100644 --- a/tests/test_gen_symbols/lib.rs +++ b/tests/test_gen_symbols/lib.rs @@ -21,7 +21,12 @@ pub fn compile_test_gl() { gl::Clear(gl::COLOR_BUFFER_BIT); let _: raw::c_uint = gl::CreateProgram(); gl::CompileShader(5); - gl::GetActiveUniformBlockiv(0, 0, gl::UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER, std::ptr::null_mut()); + gl::GetActiveUniformBlockiv( + 0, + 0, + gl::UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER, + std::ptr::null_mut(), + ); let _: *mut raw::c_void = gl::MapBuffer(0, 0); } @@ -51,11 +56,15 @@ pub fn compile_test_wgl() { pub fn compile_test_egl() { unsafe { let _ = [ - egl::SURFACE_TYPE, egl::WINDOW_BIT, - egl::BLUE_SIZE, 8, - egl::GREEN_SIZE, 8, - egl::RED_SIZE, 8, - egl::NONE + egl::SURFACE_TYPE, + egl::WINDOW_BIT, + egl::BLUE_SIZE, + 8, + egl::GREEN_SIZE, + 8, + egl::RED_SIZE, + 8, + egl::NONE, ]; let _ = egl::GetDisplay(egl::DEFAULT_DISPLAY); diff --git a/tests/test_no_warnings/build.rs b/tests/test_no_warnings/build.rs index faf59e76..ffe77e10 100644 --- a/tests/test_no_warnings/build.rs +++ b/tests/test_no_warnings/build.rs @@ -216,10 +216,11 @@ fn main() { .unwrap(); writeln!(&mut file, "}}").unwrap(); - writeln!(&mut file, - "mod egl_static_struct {{ {}", - build_egl_symbols()) - .unwrap(); + writeln!( + &mut file, + "mod egl_static_struct {{ {}", + build_egl_symbols() + ).unwrap(); egl_registry .write_bindings(StaticStructGenerator, &mut file) .unwrap(); diff --git a/tests/test_symbols/lib.rs b/tests/test_symbols/lib.rs index 3ad17a28..59a49bd5 100644 --- a/tests/test_symbols/lib.rs +++ b/tests/test_symbols/lib.rs @@ -20,10 +20,15 @@ pub mod gl { pub fn compile_test_symbols_exist() { unsafe { - gl::Clear(gl::COLOR_BUFFER_BIT); - let _: raw::c_uint = gl::CreateProgram(); - gl::CompileShader(5); - gl::GetActiveUniformBlockiv(0, 0, gl::UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER, std::ptr::null_mut()); + gl::Clear(gl::COLOR_BUFFER_BIT); + let _: raw::c_uint = gl::CreateProgram(); + gl::CompileShader(5); + gl::GetActiveUniformBlockiv( + 0, + 0, + gl::UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER, + std::ptr::null_mut(), + ); } } @@ -33,7 +38,7 @@ fn test_fallback_works() { match name { "glGenFramebuffers" => 0 as *const raw::c_void, "glGenFramebuffersEXT" => 42 as *const raw::c_void, - name => panic!("test tried to load {} unexpectedly!", name) + name => panic!("test tried to load {} unexpectedly!", name), } }; diff --git a/tests/test_webgl_stdweb/Cargo.toml b/tests/test_webgl_stdweb/Cargo.toml new file mode 100644 index 00000000..c58374bc --- /dev/null +++ b/tests/test_webgl_stdweb/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "test_webgl_stdweb" +version = "0.0.0" +build = "build.rs" +publish = false + +[lib] +path = "lib.rs" + +[dependencies] +stdweb = "0.4.0" +stdweb-derive = "0.4.0" +serde = "1.0.0" +serde_derive = "1.0.0" + +[build-dependencies] +webgl_generator = { path = "../../webgl_generator" } diff --git a/tests/test_webgl_stdweb/build.rs b/tests/test_webgl_stdweb/build.rs new file mode 100644 index 00000000..c6ae9e0a --- /dev/null +++ b/tests/test_webgl_stdweb/build.rs @@ -0,0 +1,29 @@ +// Copyright 2016 Brendan Zabarauskas and the gl-rs developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +extern crate webgl_generator; + +use webgl_generator::*; +use std::env; +use std::fs::File; +use std::path::*; + +fn main() { + let dest = env::var("OUT_DIR").unwrap(); + let mut file = File::create(&Path::new(&dest).join("test_webgl_stdweb.rs")).unwrap(); + + Registry::new(Api::WebGl2, Exts::ALL) + .write_bindings(StdwebGenerator, &mut file) + .unwrap(); +} diff --git a/tests/test_webgl_stdweb/lib.rs b/tests/test_webgl_stdweb/lib.rs new file mode 100644 index 00000000..aee9e308 --- /dev/null +++ b/tests/test_webgl_stdweb/lib.rs @@ -0,0 +1,24 @@ +// Copyright 2015 Brendan Zabarauskas and the gl-rs developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![allow(unused_parens, non_camel_case_types)] + +#[macro_use] +extern crate serde_derive as _serde_derive; +#[macro_use] +extern crate stdweb as _stdweb; +#[macro_use] +extern crate stdweb_derive as _stdweb_derive; + +include!(concat!(env!("OUT_DIR"), "/test_webgl_stdweb.rs")); diff --git a/tests/test_with_extensions/lib.rs b/tests/test_with_extensions/lib.rs index 249abbc9..49d431a4 100644 --- a/tests/test_with_extensions/lib.rs +++ b/tests/test_with_extensions/lib.rs @@ -17,31 +17,31 @@ pub mod gl { } pub fn compile_test_symbols_exist() { - let _ = gl::DebugMessageControlARB; - let _ = gl::DebugMessageInsertARB; - let _ = gl::DebugMessageCallbackARB; - let _ = gl::GetDebugMessageLogARB; + let _ = gl::DebugMessageControlARB; + let _ = gl::DebugMessageInsertARB; + let _ = gl::DebugMessageCallbackARB; + let _ = gl::GetDebugMessageLogARB; - assert_eq!(gl::DEBUG_OUTPUT_SYNCHRONOUS_ARB, 0x8242); - assert_eq!(gl::MAX_DEBUG_MESSAGE_LENGTH_ARB, 0x9143); - assert_eq!(gl::MAX_DEBUG_LOGGED_MESSAGES_ARB, 0x9144); - assert_eq!(gl::DEBUG_LOGGED_MESSAGES_ARB, 0x9145); - assert_eq!(gl::DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB, 0x8243); - assert_eq!(gl::DEBUG_CALLBACK_FUNCTION_ARB, 0x8244); - assert_eq!(gl::DEBUG_CALLBACK_USER_PARAM_ARB, 0x8245); - assert_eq!(gl::DEBUG_SOURCE_API_ARB, 0x8246); - assert_eq!(gl::DEBUG_SOURCE_WINDOW_SYSTEM_ARB, 0x8247); - assert_eq!(gl::DEBUG_SOURCE_SHADER_COMPILER_ARB, 0x8248); - assert_eq!(gl::DEBUG_SOURCE_THIRD_PARTY_ARB, 0x8249); - assert_eq!(gl::DEBUG_SOURCE_APPLICATION_ARB, 0x824A); - assert_eq!(gl::DEBUG_SOURCE_OTHER_ARB, 0x824B); - assert_eq!(gl::DEBUG_TYPE_ERROR_ARB, 0x824C); - assert_eq!(gl::DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB, 0x824D); - assert_eq!(gl::DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB, 0x824E); - assert_eq!(gl::DEBUG_TYPE_PORTABILITY_ARB, 0x824F); - assert_eq!(gl::DEBUG_TYPE_PERFORMANCE_ARB, 0x8250); - assert_eq!(gl::DEBUG_TYPE_OTHER_ARB, 0x8251); - assert_eq!(gl::DEBUG_SEVERITY_HIGH_ARB, 0x9146); - assert_eq!(gl::DEBUG_SEVERITY_MEDIUM_ARB, 0x9147); - assert_eq!(gl::DEBUG_SEVERITY_LOW_ARB, 0x9148); + assert_eq!(gl::DEBUG_OUTPUT_SYNCHRONOUS_ARB, 0x8242); + assert_eq!(gl::MAX_DEBUG_MESSAGE_LENGTH_ARB, 0x9143); + assert_eq!(gl::MAX_DEBUG_LOGGED_MESSAGES_ARB, 0x9144); + assert_eq!(gl::DEBUG_LOGGED_MESSAGES_ARB, 0x9145); + assert_eq!(gl::DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB, 0x8243); + assert_eq!(gl::DEBUG_CALLBACK_FUNCTION_ARB, 0x8244); + assert_eq!(gl::DEBUG_CALLBACK_USER_PARAM_ARB, 0x8245); + assert_eq!(gl::DEBUG_SOURCE_API_ARB, 0x8246); + assert_eq!(gl::DEBUG_SOURCE_WINDOW_SYSTEM_ARB, 0x8247); + assert_eq!(gl::DEBUG_SOURCE_SHADER_COMPILER_ARB, 0x8248); + assert_eq!(gl::DEBUG_SOURCE_THIRD_PARTY_ARB, 0x8249); + assert_eq!(gl::DEBUG_SOURCE_APPLICATION_ARB, 0x824A); + assert_eq!(gl::DEBUG_SOURCE_OTHER_ARB, 0x824B); + assert_eq!(gl::DEBUG_TYPE_ERROR_ARB, 0x824C); + assert_eq!(gl::DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB, 0x824D); + assert_eq!(gl::DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB, 0x824E); + assert_eq!(gl::DEBUG_TYPE_PORTABILITY_ARB, 0x824F); + assert_eq!(gl::DEBUG_TYPE_PERFORMANCE_ARB, 0x8250); + assert_eq!(gl::DEBUG_TYPE_OTHER_ARB, 0x8251); + assert_eq!(gl::DEBUG_SEVERITY_HIGH_ARB, 0x9146); + assert_eq!(gl::DEBUG_SEVERITY_MEDIUM_ARB, 0x9147); + assert_eq!(gl::DEBUG_SEVERITY_LOW_ARB, 0x9148); } diff --git a/webgl_generator/Cargo.toml b/webgl_generator/Cargo.toml new file mode 100644 index 00000000..55302cdc --- /dev/null +++ b/webgl_generator/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "webgl_generator" +version = "0.1.0" +authors = [ + "Diggory Blake", +] +description = "Code generators for creating bindings to the WebGL APIs." +license = "Apache-2.0" +documentation = "https://docs.rs/webgl_generator" +homepage = "https://github.com/brendanzab/gl-rs/webgl_generator/" +repository = "https://github.com/brendanzab/gl-rs/" +readme = "README.md" +categories = ["api-bindings", "rendering::graphics-api"] +keywords = ["webgl"] + +[lib] +name = "webgl_generator" +path = "lib.rs" + +[dependencies] +khronos_api = { version = "2.0.0", path = "../khronos_api" } +webidl = { version = "0.4.1" } +heck = { version = "0.3.0" } +RustyXML = "0.1.1" +html2runes = "1.0.0" +regex = "0.2.5" diff --git a/webgl_generator/README.md b/webgl_generator/README.md new file mode 100644 index 00000000..16eb2708 --- /dev/null +++ b/webgl_generator/README.md @@ -0,0 +1,19 @@ +# webgl_generator + +[![Version](https://img.shields.io/crates/v/webgl_generator.svg)](https://crates.io/crates/webgl_generator) +[![License](https://img.shields.io/crates/l/webgl_generator.svg)](https://github.com/brendanzab/gl-rs/blob/master/LICENSE) +[![Downloads](https://img.shields.io/crates/d/webgl_generator.svg)](https://crates.io/crates/webgl_generator) + +Code generators for creating bindings to the WebGL APIs. + +## Usage + +See `tests/test_webgl_stdweb` for an example of how to use these generators. + +## Generator types + +### Stdweb generator + +The stdweb generator is currently the only supported webgl generator. This generator +uses `stdweb` to bind the relevant javascript APIs, and integrates with the `stdweb` +`RenderingContext` trait. diff --git a/webgl_generator/lib.rs b/webgl_generator/lib.rs new file mode 100644 index 00000000..17069ea2 --- /dev/null +++ b/webgl_generator/lib.rs @@ -0,0 +1,33 @@ +// Copyright 2015 Brendan Zabarauskas and the gl-rs developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! A WebGL bindings generator. It defines a function named `Registry::write_bindings` which can be +//! used to generate all constants and functions of a given WebGL version. +//! +//! See the `webgl` crate for an example of use. +extern crate heck; +extern crate html2runes; +extern crate khronos_api; +extern crate regex; +extern crate webidl; +extern crate xml; + +mod utils; +mod webgl_generators; +mod webgl_registry; + +pub use webgl_generators::Generator; +pub use webgl_generators::stdweb_gen::StdwebGenerator; + +pub use webgl_registry::*; diff --git a/webgl_generator/utils.rs b/webgl_generator/utils.rs new file mode 100644 index 00000000..e7a42f9a --- /dev/null +++ b/webgl_generator/utils.rs @@ -0,0 +1,85 @@ +use std::collections::BTreeMap; +use std::str; + +use webidl::{self, ast}; + +/// Helper method for inserting into a BTreeMap-based multi-map +pub fn multimap_insert(m: &mut BTreeMap>, key: K, value: V) { + let v = m.entry(key).or_insert_with(Vec::new); + if !v.contains(&value) { + v.push(value); + } +} + +/// Helper method for appending to a BTreeMap-based multi-map +pub fn multimap_append( + m: &mut BTreeMap>, + other: BTreeMap>, +) { + for (k, v) in other { + for value in v { + multimap_insert(m, k.clone(), value); + } + } +} + +/// Best-effort attempt to render HTML into a doc-comment which can +/// be placed in the generated code. +pub fn convert_html_to_doc_comment(html: &str) -> String { + use regex::RegexBuilder; + use html2runes; + + // Create doc comments + let doc_comment_regex = RegexBuilder::new("^").multi_line(true).build().unwrap(); + + let md = html2runes::markdown::convert_string(html); + let mut doc = doc_comment_regex + .replace_all(md.trim_right(), "/// ") + .into(); + doc += "\n"; + doc +} + +/// Appends an underscore to a name if it conflicts with a reserved word. +pub fn unreserve>(name: S) -> String { + const RESERVED_WORDS: &'static [&'static str] = &[ + "as", "break", "const", "continue", "crate", "else", "enum", "extern", "false", "fn", + "for", "if", "impl", "in", "let", "loop", "match", "mod", "move", "mut", "pub", "ref", + "return", "Self", "self", "static", "struct", "super", "trait", "true", "type", "unsafe", + "use", "where", "while", "abstract", "alignof", "become", "box", "do", "final", "macro", + "offsetof", "override", "priv", "proc", "pure", "sizeof", "typeof", "unsized", "virtual", + "yield", + ]; + + let mut s = name.into(); + if RESERVED_WORDS.contains(&&*s) { + s += "_"; + } + s +} + +/// Converts to lower snake-case +pub fn snake(name: &str) -> String { + use heck::SnakeCase; + name.to_snake_case() +} + +/// Converts to upper snake-case +pub fn shouty_snake(name: &str) -> String { + use heck::ShoutySnakeCase; + name.to_shouty_snake_case() +} + +/// Converts to upper camel case +pub fn camel(name: &str) -> String { + use heck::CamelCase; + name.to_camel_case() +} + +/// Parse WebIDL content into an AST +pub fn parse_defs(src: &[u8]) -> Vec { + let src = str::from_utf8(src).expect("IDL contained invalid UTF-8"); + + let parser = webidl::Parser::new(); + parser.parse_string(src).expect("Failed to parse IDL") +} diff --git a/webgl_generator/webgl_generators/mod.rs b/webgl_generator/webgl_generators/mod.rs new file mode 100644 index 00000000..b10fa8bb --- /dev/null +++ b/webgl_generator/webgl_generators/mod.rs @@ -0,0 +1,27 @@ +// Copyright 2015 Brendan Zabarauskas and the gl-rs developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::io; + +use webgl_registry::Registry; + +pub mod stdweb_gen; + +/// Trait for a webgl bindings generator. +pub trait Generator { + /// Builds the WebGL bindings. + fn write(&self, registry: &Registry, dest: &mut W) -> io::Result<()> + where + W: io::Write; +} diff --git a/webgl_generator/webgl_generators/stdweb_gen.rs b/webgl_generator/webgl_generators/stdweb_gen.rs new file mode 100644 index 00000000..631c380c --- /dev/null +++ b/webgl_generator/webgl_generators/stdweb_gen.rs @@ -0,0 +1,832 @@ +// Copyright 2015 Brendan Zabarauskas and the gl-rs developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::io; +use std::collections::BTreeSet; + +use utils::*; +use webgl_registry::*; + +#[allow(missing_copy_implementations)] +#[derive(Debug)] +pub struct StdwebGenerator; + +#[derive(Clone, Debug)] +struct GenericContext { + args: BTreeSet, + constraints: Vec, +} + +impl GenericContext { + pub fn new() -> Self { + GenericContext { + args: BTreeSet::new(), + constraints: Vec::new(), + } + } + pub fn arg(&mut self, desired_name: &str) -> String { + for i in 0.. { + let name = format!("{}{}", desired_name, i); + if !self.args.contains(&name) { + self.args.insert(name.clone()); + return name; + } + } + unreachable!() + } + pub fn constrain(&mut self, constraint: String) { + self.constraints.push(constraint); + } + pub fn args(&self) -> String { + if self.args.is_empty() { + String::new() + } else { + let args: Vec<_> = self.args.iter().cloned().collect(); + format!("<{}>", args.join(", ")) + } + } + pub fn constraints(&self) -> String { + if self.constraints.is_empty() { + String::new() + } else { + format!(" where {}", self.constraints.join(", ")) + } + } +} + +#[derive(Clone, Debug)] +enum ArgWrapper { + None, + AsTypedArray, + Optional(Box), + Sequence(Box), + DoubleCast, + Once, +} + +impl ArgWrapper { + fn wrap(&self, arg: &str) -> String { + match self { + &ArgWrapper::None => arg.into(), + &ArgWrapper::AsTypedArray => format!("unsafe {{ {}.as_typed_array() }}", arg), + &ArgWrapper::Optional(ref inner) => { + format!("{}.map(|inner| {})", arg, inner.wrap("inner")) + } + &ArgWrapper::Sequence(ref inner) => format!( + "{}.iter().map(|inner| {}).collect::>()", + arg, + inner.wrap("inner") + ), + &ArgWrapper::DoubleCast => format!("({} as f64)", arg), + &ArgWrapper::Once => format!("Once({})", arg), + } + } +} + +#[derive(Clone, Debug)] +struct ProcessedArg { + type_: String, + wrapper: ArgWrapper, + optional: bool, +} + +impl ProcessedArg { + fn simple>(name: S) -> ProcessedArg { + ProcessedArg { + type_: name.into(), + wrapper: ArgWrapper::None, + optional: false, + } + } +} + +fn process_arg_type_kind( + type_kind: &TypeKind, + registry: &Registry, + gc: &mut GenericContext, +) -> ProcessedArg { + let (name, flat_kind) = type_kind.flatten(registry); + match flat_kind { + &TypeKind::Primitive(ref p) => match p { + &Primitive::I64 => ProcessedArg { + type_: name.unwrap().into(), + wrapper: ArgWrapper::DoubleCast, + optional: false, + }, + &Primitive::U64 => ProcessedArg { + type_: name.unwrap().into(), + wrapper: ArgWrapper::DoubleCast, + optional: false, + }, + _ => ProcessedArg::simple(name.unwrap()), + }, + &TypeKind::String => ProcessedArg::simple("&str"), + &TypeKind::ArrayBuffer | &TypeKind::ArrayBufferView => ProcessedArg::simple("&ArrayBuffer"), + &TypeKind::BufferSource => ProcessedArg::simple("&ArrayBuffer"), + &TypeKind::CanvasElement => ProcessedArg::simple("&CanvasElement"), + &TypeKind::TypedArray(ref p) => { + let lt = gc.arg("'a"); + let gp = gc.arg("T"); + gc.constrain(format!("{}: AsTypedArray<{}, {}>", gp, lt, p.name())); + ProcessedArg { + type_: gp, + wrapper: ArgWrapper::AsTypedArray, + optional: false, + } + } + &TypeKind::Sequence(ref t) => { + let inner = process_arg_type(t, registry, gc); + ProcessedArg { + type_: format!("&[{}]", inner.type_), + wrapper: match inner.wrapper { + ArgWrapper::None => ArgWrapper::None, + other => ArgWrapper::Sequence(Box::new(other)), + }, + optional: false, + } + } + &TypeKind::Union(ref ts) => { + let t = ts.iter() + .filter_map(|t| match t.kind { + TypeKind::TypedArray(_) => Some(t), + TypeKind::Sequence(_) => None, + _ => panic!("Union support is limited!"), + }) + .next() + .expect("Union did not contain a TypedArray"); + + process_arg_type(t, registry, gc) + } + &TypeKind::Named(ref actual_name) => { + match registry.resolve_type(actual_name) { + &NamedType::Dictionary(_) | &NamedType::Interface(_) => { + ProcessedArg::simple(format!("&{}", name.unwrap())) + } + &NamedType::Enum(_) => ProcessedArg::simple(name.unwrap()), + &NamedType::Typedef(ref t) => { + // We have to "look through" the typedef, as the correct parameter + // type is not representable using the alias. + assert!(t.optional); + process_arg_type(t, registry, gc) + } + &NamedType::Callback(_) => { + let gp = gc.arg("F"); + gc.constrain(format!("{}: FnOnce() + 'static", gp)); + ProcessedArg { + type_: gp, + wrapper: ArgWrapper::Once, + optional: false, + } + } + &NamedType::Mixin(_) => panic!("Mixins are not usable as types!"), + } + } + &TypeKind::Any | &TypeKind::Object => { + let gp = gc.arg("T"); + gc.constrain(format!("{}: JsSerialize", gp)); + ProcessedArg::simple(gp) + } + } +} + +fn process_arg_type(type_: &Type, registry: &Registry, gc: &mut GenericContext) -> ProcessedArg { + let mut result = process_arg_type_kind(&type_.kind, registry, gc); + if type_.optional && !result.optional { + result.type_ = format!("Option<{}>", result.type_); + result.wrapper = match result.wrapper { + ArgWrapper::None => ArgWrapper::None, + other => ArgWrapper::Optional(Box::new(other)), + }; + result.optional = true; + } + result +} + +#[derive(Clone, Debug)] +enum ResultWrapper { + TryInto, + Ok, +} + +impl ResultWrapper { + fn wrap(&self, content: &str) -> String { + match self { + &ResultWrapper::TryInto => format!("{}.try_into().unwrap()", content), + &ResultWrapper::Ok => format!("{}.try_into().ok()", content), + } + } +} + +#[derive(Clone, Debug)] +struct ProcessedResult { + type_: String, + wrapper: ResultWrapper, + optional: bool, +} + +impl ProcessedResult { + fn simple>(name: S) -> ProcessedResult { + ProcessedResult { + type_: name.into(), + wrapper: ResultWrapper::TryInto, + optional: false, + } + } +} + +fn process_result_type_kind(type_kind: &TypeKind, registry: &Registry) -> ProcessedResult { + match type_kind { + &TypeKind::Primitive(ref p) => ProcessedResult::simple(p.name()), + &TypeKind::String => ProcessedResult::simple("String"), + &TypeKind::ArrayBuffer | &TypeKind::ArrayBufferView => { + ProcessedResult::simple("ArrayBuffer") + } + &TypeKind::BufferSource => unimplemented!("BufferSource not supported in output"), + &TypeKind::CanvasElement => ProcessedResult::simple("CanvasElement"), + &TypeKind::TypedArray(ref p) => { + ProcessedResult::simple(format!("TypedArray<{}>", p.name())) + } + &TypeKind::Sequence(ref t) => { + let inner = process_result_type(t, registry); + ProcessedResult::simple(format!("Vec<{}>", inner.type_)) + } + &TypeKind::Union(ref ts) => { + let t = ts.iter() + .filter_map(|t| match t.kind { + TypeKind::TypedArray(_) => Some(t), + TypeKind::Sequence(_) => None, + _ => panic!("Union support is limited!"), + }) + .next() + .expect("Union did not contain a TypedArray"); + + process_result_type(t, registry) + } + &TypeKind::Named(ref name) => match registry.resolve_type(name) { + &NamedType::Dictionary(_) | &NamedType::Interface(_) | &NamedType::Enum(_) => { + ProcessedResult::simple(name.as_str()) + } + &NamedType::Typedef(ref t) => { + let inner = process_result_type(t, registry); + ProcessedResult { + type_: name.clone(), + wrapper: inner.wrapper.clone(), + optional: inner.optional, + } + } + &NamedType::Callback(_) => unimplemented!(), + &NamedType::Mixin(_) => panic!("Mixins are not usable as types!"), + }, + &TypeKind::Any | &TypeKind::Object => ProcessedResult::simple("Value"), + } +} + +fn process_result_type(type_: &Type, registry: &Registry) -> ProcessedResult { + let mut result = process_result_type_kind(&type_.kind, registry); + if type_.optional && !result.optional { + result.type_ = format!("Option<{}>", result.type_); + result.wrapper = ResultWrapper::Ok; + result.optional = true; + } + result +} + +fn write_header(registry: &Registry, dest: &mut W) -> io::Result<()> +where + W: io::Write, +{ + writeln!( + dest, + r#" +// {registry:?} +extern crate stdweb; + +use self::stdweb::{{Reference, Value, UnsafeTypedArray, Once, JsSerialize, InstanceOf}}; +use self::stdweb::unstable::{{TryFrom, TryInto}}; +use self::stdweb::web::{{RenderingContext, TypedArray, ArrayBuffer}}; +use self::stdweb::web::html_element::CanvasElement; + +type ConversionError = >::Error; + +pub trait AsTypedArray<'a, T> {{ + type Result: JsSerialize; + + unsafe fn as_typed_array(self) -> Self::Result; +}} + +pub trait Extension: TryFrom {{ + const NAME: &'static str; +}} + +macro_rules! define_array {{ + ($elem:ty) => {{ + impl<'a> AsTypedArray<'a, $elem> for &'a TypedArray<$elem> {{ + type Result = Self; + + unsafe fn as_typed_array(self) -> Self::Result {{ self }} + }} + + impl<'a> AsTypedArray<'a, $elem> for &'a [$elem] {{ + type Result = UnsafeTypedArray<'a, $elem>; + + unsafe fn as_typed_array(self) -> Self::Result {{ UnsafeTypedArray::new(self) }} + }} + }} +}} + +define_array!(i8); +define_array!(u8); +define_array!(i16); +define_array!(u16); +define_array!(i32); +define_array!(u32); +define_array!(f32); +define_array!(f64); + "#, + registry = registry + )?; + Ok(()) +} + +impl super::Generator for StdwebGenerator { + fn write(&self, registry: &Registry, dest: &mut W) -> io::Result<()> + where + W: io::Write, + { + write_header(registry, dest)?; + write_typedefs(registry, dest)?; + write_enums(registry, dest)?; + write_dictionaries(registry, dest)?; + write_interfaces(registry, dest)?; + write_extensions(registry, dest)?; + Ok(()) + } +} + +fn write_typedefs(registry: &Registry, dest: &mut W) -> io::Result<()> +where + W: io::Write, +{ + for (name, t) in registry.iter_types(NamedType::as_typedef) { + write_typedef(name, t, registry, dest)?; + } + Ok(()) +} + +fn write_typedef(name: &str, type_: &Type, registry: &Registry, dest: &mut W) -> io::Result<()> +where + W: io::Write, +{ + writeln!( + dest, + r#"pub type {name} = {type_};"#, + name = name, + type_ = process_result_type(type_, registry).type_ + )?; + Ok(()) +} + +fn write_enums(registry: &Registry, dest: &mut W) -> io::Result<()> +where + W: io::Write, +{ + for (name, enum_) in registry.iter_types(NamedType::as_enum) { + write_enum(name, enum_, registry, dest)?; + } + Ok(()) +} + +fn write_enum(name: &str, enum_: &Enum, _registry: &Registry, dest: &mut W) -> io::Result<()> +where + W: io::Write, +{ + write!( + dest, + r#" +#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] +pub enum {name} {{ + "#, + name = name + )?; + + for variant in &enum_.variants { + writeln!( + dest, + r#" + #[serde(rename = "{raw_variant}")] + {variant},"#, + variant = camel(&variant), + raw_variant = variant + )?; + } + + writeln!( + dest, + r#" +}} +js_deserializable!({name}); +js_serializable!({name}); + "#, + name = name + )?; + Ok(()) +} + +fn write_dictionaries(registry: &Registry, dest: &mut W) -> io::Result<()> +where + W: io::Write, +{ + for (name, dictionary) in registry.iter_types(NamedType::as_dictionary) { + write_dictionary(name, dictionary, registry, dest)?; + } + Ok(()) +} + +fn write_dictionary( + name: &str, + dictionary: &Dictionary, + registry: &Registry, + dest: &mut W, +) -> io::Result<()> +where + W: io::Write, +{ + if dictionary.is_hidden { + return Ok(()); + } + + write!( + dest, + r#" +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct {name} {{ + "#, + name = name + )?; + + for (name, field) in dictionary.collect_fields(registry) { + write_field(name, field, registry, dest)?; + } + + writeln!( + dest, + r#" +}} +js_deserializable!({name}); +js_serializable!({name}); + "#, + name = name + )?; + Ok(()) +} + +fn write_field(name: &str, field: &Field, registry: &Registry, dest: &mut W) -> io::Result<()> +where + W: io::Write, +{ + let mut serde_attrs = Vec::new(); + let field_name = unreserve(snake(name)); + if field_name != name { + serde_attrs.push(format!(r#"rename = "{}""#, name)); + } + let field_type = process_result_type(&field.type_, registry); + if field_type.optional { + serde_attrs.push(r#"default"#.into()); + serde_attrs.push(r#"skip_serializing_if = "Option::is_none""#.into()); + } + + if !serde_attrs.is_empty() { + write!( + dest, + r#" + #[serde({})]"#, + serde_attrs.join(", ") + )?; + } + + writeln!( + dest, + r#" + {name}: {type_},"#, + name = field_name, + type_ = field_type.type_ + )?; + + Ok(()) +} + +fn write_interfaces(registry: &Registry, dest: &mut W) -> io::Result<()> +where + W: io::Write, +{ + for (name, interface) in registry.iter_types(NamedType::as_interface) { + write_interface(name, interface, registry, dest)?; + } + Ok(()) +} + +fn write_interface( + name: &str, + interface: &Interface, + registry: &Registry, + dest: &mut W, +) -> io::Result<()> +where + W: io::Write, +{ + if interface.is_hidden { + return Ok(()); + } + + let mut attrs = String::new(); + let custom_instance_check = if name == "GLContext" { + Some("[WebGLRenderingContext, WebGL2RenderingContext].includes( @{{reference}}.constructor )".into()) + } else if interface.has_class { + attrs += &format!("#[reference(instance_of = {:?})]\n", name); + None + } else { + Some(format!("@{{reference}}.constructor.name == {:?}", name)) + }; + + write!( + dest, + r#" +{doc_comment}#[derive(Debug, Clone, ReferenceType)] +{attrs}pub struct {name}(Reference); + +impl {name} {{ + "#, + name = name, + attrs = attrs, + doc_comment = interface.doc_comment + )?; + + for (name, members) in interface.collect_members(registry, &VisitOptions::default()) { + for (index, member) in members.into_iter().enumerate() { + match member { + &Member::Const(ref const_) => { + assert!(index == 0); + write_const(&name, const_, registry, dest)?; + } + &Member::Attribute(ref attribute) => { + assert!(index == 0); + write_attribute(&name, attribute, registry, dest)?; + } + &Member::Operation(ref operation) => { + write_operation(&name, index, operation, registry, dest)?; + } + } + } + } + + writeln!( + dest, + r#" +}} + "# + )?; + + if let Some(instance_check) = custom_instance_check { + write!( + dest, + r#" +impl InstanceOf for {name} {{ + #[inline] + fn instance_of( reference: &Reference ) -> bool {{ + js!( + return {instance_check}; + ).try_into().unwrap() + }} +}} + "#, + name = name, + instance_check = instance_check + )?; + } + + if let Some(rendering_context) = interface.rendering_context { + writeln!( + dest, + r#"impl RenderingContext for {name} {{ + type Error = ConversionError; + fn from_canvas(canvas: &CanvasElement) -> Result {{ + js!( + return @{{canvas}}.getContext("{rendering_context}"); + ).try_into() + }} +}} + "#, + name = name, + rendering_context = rendering_context + )?; + } + + Ok(()) +} + +fn write_const(name: &str, const_: &Const, registry: &Registry, dest: &mut W) -> io::Result<()> +where + W: io::Write, +{ + let const_type = process_result_type(&const_.type_, registry); + write!( + dest, + r#" + pub const {name}: {type_} = {value};"#, + name = shouty_snake(name), + type_ = const_type.type_, + value = const_.value + )?; + Ok(()) +} + +fn write_attribute( + name: &str, + attribute: &Attribute, + registry: &Registry, + dest: &mut W, +) -> io::Result<()> +where + W: io::Write, +{ + if attribute.getter { + let result_type = process_result_type(&attribute.type_, registry); + let expr = result_type.wrapper.wrap(&format!( + "(js! {{ return @{{self}}.{raw_name}; }} )", + raw_name = name + )); + + write!( + dest, + r#" + + pub fn {name}(&self) -> {type_} {{ + {expr} + }}"#, + name = unreserve(snake(name)), + type_ = result_type.type_, + expr = expr + )?; + } + if attribute.setter { + let mut gc = GenericContext::new(); + let arg_type = process_arg_type(&attribute.type_, registry, &mut gc); + write!( + dest, + r#" + + pub fn set_{name}{gargs}(&self, value: {type_}){gwhere} {{ + js!( @{{self}}.{raw_name} = @{{{value}}}; ); + }}"#, + name = snake(name), + raw_name = name, + type_ = arg_type.type_, + gargs = gc.args(), + gwhere = gc.constraints(), + value = arg_type.wrapper.wrap("value") + )?; + } + Ok(()) +} + +fn write_get_extension(dest: &mut W) -> io::Result<()> +where + W: io::Write, +{ + write!( + dest, + r#" + + pub fn get_extension(&self) -> Option {{ + (js! {{ return @{{self}}.getExtension({{E::NAME}}); }} ).try_into().ok() + }}"# + ) +} + +fn write_operation( + name: &str, + index: usize, + operation: &Operation, + registry: &Registry, + dest: &mut W, +) -> io::Result<()> +where + W: io::Write, +{ + match name { + "getExtension" => return write_get_extension(dest), + _ => {} + } + + let mut rust_name = unreserve(snake(name)); + if index > 0 { + rust_name = format!("{}_{}", rust_name, index); + } + + let mut gc = GenericContext::new(); + + struct OperationArg { + arg: String, + js_arg: String, + } + + let args: Vec<_> = operation + .args + .iter() + .map(|a| { + let processed = process_arg_type(&a.type_, registry, &mut gc); + let arg = format!("{}: {}", unreserve(snake(&a.name)), processed.type_); + let js_arg = format!( + "@{{{}}}", + processed.wrapper.wrap(&unreserve(snake(&a.name))) + ); + OperationArg { arg, js_arg } + }) + .collect(); + + let rust_args = args.iter() + .map(|a| a.arg.clone()) + .collect::>() + .join(", "); + let js_args = args.iter() + .map(|a| a.js_arg.clone()) + .collect::>() + .join(", "); + + if let Some(return_type) = operation.return_type.as_ref() { + let result_type = process_result_type(return_type, registry); + let expr = result_type.wrapper.wrap(&format!( + "(js! {{ return @{{self}}.{raw_name}({js_args}); }} )", + raw_name = name, + js_args = js_args + )); + + write!( + dest, + r#" + + {doc_comment}pub fn {name}{gargs}(&self, {args}) -> {return_type}{gwhere} {{ + {expr} + }}"#, + name = rust_name, + gargs = gc.args(), + args = rust_args, + return_type = result_type.type_, + gwhere = gc.constraints(), + expr = expr, + doc_comment = operation.doc_comment + )?; + } else { + write!( + dest, + r#" + + {doc_comment}pub fn {name}{gargs}(&self, {args}){gwhere} {{ + js!( @{{self}}.{raw_name}({js_args}); ); + }}"#, + name = rust_name, + raw_name = name, + gargs = gc.args(), + args = rust_args, + gwhere = gc.constraints(), + js_args = js_args, + doc_comment = operation.doc_comment + )?; + } + Ok(()) +} + +fn write_extensions(registry: &Registry, dest: &mut W) -> io::Result<()> +where + W: io::Write, +{ + for name in ®istry.extensions { + write_extension(name, registry, dest)?; + } + Ok(()) +} + +fn write_extension(name: &str, _registry: &Registry, dest: &mut W) -> io::Result<()> +where + W: io::Write, +{ + writeln!( + dest, + r#" +impl Extension for {name} {{ + const NAME: &'static str = "{name}"; +}}"#, + name = name + ) +} diff --git a/webgl_generator/webgl_registry/mod.rs b/webgl_generator/webgl_registry/mod.rs new file mode 100644 index 00000000..cc388fe6 --- /dev/null +++ b/webgl_generator/webgl_registry/mod.rs @@ -0,0 +1,126 @@ +// Copyright 2015 Brendan Zabarauskas and the gl-rs developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{fmt, str}; +use std::collections::BTreeMap; + +use khronos_api; + +use utils::convert_html_to_doc_comment; + +pub use self::types::*; +pub use self::named::*; +pub use self::registry::*; + +mod types; +mod named; +mod registry; + +const HIDDEN_NAMES: &'static [&'static str] = &["WebGLObject", "WebGLContextEventInit"]; +const RENDERING_CONTEXTS: &'static [(&'static str, &'static str)] = &[ + ("webgl", "WebGLRenderingContext"), + ("webgl2", "WebGL2RenderingContext"), +]; + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Api { + WebGl, + WebGl2, +} + +#[derive(Debug)] +struct ExtensionIDL { + pub name: String, + pub idl: String, + pub overview: String, + pub new_funs: BTreeMap, +} + +pub enum Exts<'a> { + Include(&'a [&'a str]), + Exclude(&'a [&'a str]), +} + +impl<'a> Exts<'a> { + pub const NONE: Exts<'a> = Exts::Include(&[]); + pub const ALL: Exts<'a> = Exts::Exclude(&[]); + + fn enumerate(&self) -> Vec { + use regex::{Regex, RegexBuilder}; + use xml; + + // The Khronos IDL files are... not quite right, so let's fix them up! + let enum_regex = Regex::new("([(, ])enum\\b").unwrap(); + let missing_semicolon_regex = RegexBuilder::new("^}$").multi_line(true).build().unwrap(); + let shared_callback_regex = Regex::new("\\bAcquireResourcesCallback\\b").unwrap(); + + let mut result = Vec::new(); + for &ext_xml in khronos_api::WEBGL_EXT_XML { + let elem: xml::Element = str::from_utf8(ext_xml).unwrap().parse().unwrap(); + + let overview_html = format!("{}", elem.get_child("overview", None).unwrap()); + + let mut new_funs = BTreeMap::new(); + for new_fun in elem.get_children("newfun", None) { + for f in new_fun.get_children("function", None) { + let name = f.get_attribute("name", None).unwrap().into(); + let f_html = format!("{}", f); + new_funs.insert(name, convert_html_to_doc_comment(&f_html)); + } + } + + let mut ext = ExtensionIDL { + name: elem.get_child("name", None).unwrap().content_str(), + idl: elem.get_child("idl", None).unwrap().content_str(), + overview: format!( + "/// Extension\n/// \n{}", + convert_html_to_doc_comment(&overview_html) + ), + new_funs, + }; + + if match self { + &Exts::Include(names) => names.contains(&&*ext.name), + &Exts::Exclude(names) => !names.contains(&&*ext.name), + } { + ext.idl = enum_regex.replace_all(&ext.idl, "${1}GLenum").into(); + ext.idl = missing_semicolon_regex.replace_all(&ext.idl, "};").into(); + ext.idl = shared_callback_regex + .replace_all(&ext.idl, "AcquireSharedResourcesCallback") + .into(); + result.push(ext); + } + } + + result + } +} + +impl Api { + fn idl_consts(&self) -> &'static [&'static [u8]] { + match *self { + Api::WebGl => &[khronos_api::WEBGL_IDL], + Api::WebGl2 => &[khronos_api::WEBGL_IDL, khronos_api::WEBGL2_IDL], + } + } +} + +impl fmt::Display for Api { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match *self { + Api::WebGl => write!(fmt, "webgl"), + Api::WebGl2 => write!(fmt, "webgl2"), + } + } +} diff --git a/webgl_generator/webgl_registry/named.rs b/webgl_generator/webgl_registry/named.rs new file mode 100644 index 00000000..af116979 --- /dev/null +++ b/webgl_generator/webgl_registry/named.rs @@ -0,0 +1,265 @@ +// Copyright 2015 Brendan Zabarauskas and the gl-rs developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::collections::{BTreeMap, BTreeSet}; + +use utils::{multimap_append, multimap_insert}; +use super::{Registry, Type}; + +#[derive(Debug, Clone)] +pub enum NamedType { + Mixin(Mixin), + Interface(Interface), + Dictionary(Dictionary), + Enum(Enum), + Typedef(Type), + Callback(Callback), +} + +#[derive(Debug, Clone)] +pub struct Mixin { + pub members: BTreeMap>, +} + +#[derive(Debug, Clone)] +pub struct Interface { + pub inherits: Option, + pub mixins: BTreeSet, + pub members: BTreeMap>, + pub is_hidden: bool, + pub has_class: bool, + pub rendering_context: Option<&'static str>, + pub doc_comment: String, +} + +#[derive(Debug, Clone)] +pub struct Dictionary { + pub inherits: Option, + pub fields: BTreeMap, + pub is_hidden: bool, +} + +#[derive(Debug, Clone)] +pub struct Enum { + pub variants: BTreeSet, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Callback { + pub args: Vec, + pub return_type: Option, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Const { + pub type_: Type, + pub value: String, +} + +#[derive(Debug, Eq, Clone)] +pub struct Argument { + pub name: String, + pub optional: bool, + pub type_: Type, + pub variadic: bool, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Operation { + pub args: Vec, + pub return_type: Option, + pub doc_comment: String, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Attribute { + pub type_: Type, + pub setter: bool, + pub getter: bool, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum Member { + Const(Const), + Operation(Operation), + Attribute(Attribute), +} + +#[derive(Debug, Clone)] +pub struct Field { + pub type_: Type, +} + +#[derive(Debug)] +pub struct VisitOptions { + pub visit_mixins: bool, +} + +impl NamedType { + pub fn as_mixin(&self) -> Option<&Mixin> { + if let &NamedType::Mixin(ref m) = self { + Some(m) + } else { + None + } + } + pub fn as_interface(&self) -> Option<&Interface> { + if let &NamedType::Interface(ref i) = self { + Some(i) + } else { + None + } + } + pub fn as_dictionary(&self) -> Option<&Dictionary> { + if let &NamedType::Dictionary(ref d) = self { + Some(d) + } else { + None + } + } + pub fn as_enum(&self) -> Option<&Enum> { + if let &NamedType::Enum(ref e) = self { + Some(e) + } else { + None + } + } + pub fn as_typedef(&self) -> Option<&Type> { + if let &NamedType::Typedef(ref t) = self { + Some(t) + } else { + None + } + } + pub fn as_mixin_mut(&mut self) -> Option<&mut Mixin> { + if let &mut NamedType::Mixin(ref mut m) = self { + Some(m) + } else { + None + } + } + pub fn as_interface_mut(&mut self) -> Option<&mut Interface> { + if let &mut NamedType::Interface(ref mut i) = self { + Some(i) + } else { + None + } + } + pub fn as_dictionary_mut(&mut self) -> Option<&mut Dictionary> { + if let &mut NamedType::Dictionary(ref mut d) = self { + Some(d) + } else { + None + } + } + pub fn as_enum_mut(&mut self) -> Option<&mut Enum> { + if let &mut NamedType::Enum(ref mut e) = self { + Some(e) + } else { + None + } + } + pub fn as_typedef_mut(&mut self) -> Option<&mut Type> { + if let &mut NamedType::Typedef(ref mut t) = self { + Some(t) + } else { + None + } + } +} + +impl PartialEq for Argument { + fn eq(&self, other: &Self) -> bool { + self.type_ == other.type_ + } +} + +impl Default for VisitOptions { + fn default() -> Self { + VisitOptions { visit_mixins: true } + } +} + +impl Dictionary { + pub fn collect_fields<'a>(&'a self, registry: &'a Registry) -> BTreeMap<&'a str, &'a Field> { + let mut fields = BTreeMap::new(); + + // Inherits + if let Some(inherit_name) = self.inherits.as_ref() { + let inherit = registry + .types + .get(inherit_name) + .and_then(NamedType::as_dictionary) + .expect(inherit_name); + fields.append(&mut inherit.collect_fields(registry)); + } + + // Fields + for (name, field) in &self.fields { + fields.insert(name, field); + } + + fields + } +} + +impl Interface { + pub fn collect_members<'a>( + &'a self, + registry: &'a Registry, + options: &VisitOptions, + ) -> BTreeMap<&'a str, Vec<&'a Member>> { + let mut members = BTreeMap::new(); + + // Mixins + for mixin_name in &self.mixins { + let mixin = registry + .types + .get(mixin_name) + .and_then(NamedType::as_mixin) + .expect(mixin_name); + if options.visit_mixins { + multimap_append(&mut members, mixin.collect_members(registry, options)); + } + } + + // Members + for (name, ms) in &self.members { + for member in ms { + multimap_insert(&mut members, &**name, member); + } + } + + members + } +} + +impl Mixin { + pub fn collect_members<'a>( + &'a self, + _registry: &'a Registry, + _options: &VisitOptions, + ) -> BTreeMap<&'a str, Vec<&'a Member>> { + let mut members = BTreeMap::new(); + + // Members + for (name, ms) in &self.members { + for member in ms { + multimap_insert(&mut members, &**name, member); + } + } + + members + } +} diff --git a/webgl_generator/webgl_registry/registry.rs b/webgl_generator/webgl_registry/registry.rs new file mode 100644 index 00000000..37089430 --- /dev/null +++ b/webgl_generator/webgl_registry/registry.rs @@ -0,0 +1,499 @@ +// Copyright 2015 Brendan Zabarauskas and the gl-rs developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::io; +use std::collections::{BTreeMap, BTreeSet}; +use std::collections::btree_map::{self, Entry}; + +use webidl::ast; + +use utils::{multimap_insert, parse_defs}; +use webgl_generators::Generator; + +use super::types::{Primitive, Type, TypeKind}; +use super::named::{Argument, Attribute, Callback, Const, Dictionary, Enum, Field, Interface, + Member, Mixin, NamedType, Operation}; +use super::{Api, Exts, HIDDEN_NAMES, RENDERING_CONTEXTS}; + +#[derive(Debug, Default)] +pub struct Registry { + pub types: BTreeMap, + pub extensions: BTreeSet, +} + +/// Helper for iterating over specific kinds of named type +pub struct TypeIter<'a, T: 'a, F: FnMut(&'a NamedType) -> Option<&'a T>> { + inner: btree_map::Iter<'a, String, NamedType>, + f: F, +} + +impl<'a, T: 'a, F: FnMut(&'a NamedType) -> Option<&'a T>> Iterator for TypeIter<'a, T, F> { + type Item = (&'a String, &'a T); + fn next(&mut self) -> Option { + // Implement a variation of `flat_map` + while let Some((k, v)) = self.inner.next() { + if let Some(t) = (self.f)(v) { + return Some((k, t)); + } + } + None + } +} + +impl Registry { + /// Construct a new registry given a maximum API version + /// and a set of extensions to support. + pub fn new(api: Api, exts: Exts) -> Registry { + let mut result = Registry::default(); + + // First load definitions from all API versions + // up to the one requested. + for idl_const in api.idl_consts() { + for def in parse_defs(idl_const) { + result.load_definition(def); + } + } + + // Next find all requested extensions + for ext in exts.enumerate() { + // Make a note that we included this extension + result.extensions.insert(ext.name.clone()); + + // Load the definitions + for def in parse_defs(ext.idl.as_bytes()) { + result.load_definition(def); + } + + // Attach overview doc comment + let ext_iface = result + .types + .get_mut(&ext.name) + .and_then(NamedType::as_interface_mut) + .expect(&ext.name); + ext_iface.doc_comment = ext.overview; + + // Attach individual function doc comments + for (name, new_fun) in ext.new_funs { + let members = ext_iface.members.get_mut(&name).unwrap(); + for member in members { + if let Member::Operation(ref mut op) = *member { + op.doc_comment = new_fun.clone(); + } + } + } + } + + // Find the latest version of the rendering context, and generate a + // version-independent rendering context helper type called "GLContext". + for name in RENDERING_CONTEXTS.into_iter().rev() { + if let Some(NamedType::Interface(mut iface)) = result.types.get(name.1).cloned() { + iface.rendering_context = None; + result + .types + .insert("GLContext".into(), NamedType::Interface(iface)); + break; + } + } + + // Hide types that are listed in our "hidden names" array + for &hidden_name in HIDDEN_NAMES { + if let Some(interface) = result + .types + .get_mut(hidden_name) + .and_then(NamedType::as_interface_mut) + { + interface.is_hidden = true; + } + if let Some(dictionary) = result + .types + .get_mut(hidden_name) + .and_then(NamedType::as_dictionary_mut) + { + dictionary.is_hidden = true; + } + } + + // Done! + result + } + + /// Iterator over types matched by a filtering function + pub fn iter_types<'a, T, F: FnMut(&'a NamedType) -> Option<&'a T>>( + &'a self, + f: F, + ) -> TypeIter<'a, T, F> { + TypeIter { + inner: self.types.iter(), + f, + } + } + + /// Resolves a named type + pub fn resolve_type(&self, name: &str) -> &NamedType { + self.types.get(name).expect(name) + } + + /// Use the specified generator to generate bindings from this registry + /// and write them to a stream. + pub fn write_bindings(&self, generator: G, output: &mut W) -> io::Result<()> + where + G: Generator, + W: io::Write, + { + generator.write(&self, output) + } + + fn load_const(&mut self, const_: ast::Const) -> Option<(String, Member)> { + use self::ast::{ConstType, ConstValue}; + + let type_ = ast::Type { + extended_attributes: Vec::new(), + kind: match const_.type_ { + ConstType::Boolean => ast::TypeKind::Boolean, + ConstType::Byte => ast::TypeKind::Byte, + ConstType::Octet => ast::TypeKind::Octet, + ConstType::RestrictedDouble => ast::TypeKind::RestrictedDouble, + ConstType::UnrestrictedDouble => ast::TypeKind::UnrestrictedDouble, + ConstType::RestrictedFloat => ast::TypeKind::RestrictedFloat, + ConstType::UnrestrictedFloat => ast::TypeKind::UnrestrictedFloat, + ConstType::SignedLong => ast::TypeKind::SignedLong, + ConstType::UnsignedLong => ast::TypeKind::UnsignedLong, + ConstType::SignedLongLong => ast::TypeKind::SignedLongLong, + ConstType::UnsignedLongLong => ast::TypeKind::UnsignedLongLong, + ConstType::SignedShort => ast::TypeKind::SignedShort, + ConstType::UnsignedShort => ast::TypeKind::UnsignedShort, + ConstType::Identifier(s) => ast::TypeKind::Identifier(s), + }, + nullable: const_.nullable, + }; + + Some(( + const_.name, + Member::Const(Const { + type_: self.load_type(type_), + value: match const_.value { + ConstValue::BooleanLiteral(b) => format!("{:?}", b), + ConstValue::FloatLiteral(v) => format!("{:?}", v), + ConstValue::IntegerLiteral(v) => format!("{:?}", v), + ConstValue::Null => "None".into(), + }, + }), + )) + } + + fn load_attribute(&mut self, attribute: ast::Attribute) -> Option<(String, Member)> { + use self::ast::Attribute::*; + match attribute { + Regular(a) => { + let type_ = self.load_type(*a.type_); + Some(( + a.name, + Member::Attribute(Attribute { + type_, + setter: !a.read_only, + getter: !a.inherits, + }), + )) + } + _ => None, + } + } + + fn load_argument(&mut self, argument: ast::Argument) -> Argument { + let type_ = self.load_type(*argument.type_); + Argument { + name: argument.name, + optional: argument.optional, + type_, + variadic: argument.variadic, + } + } + + fn load_operation(&mut self, operation: ast::Operation) -> Option<(String, Member)> { + use self::ast::Operation::*; + use self::ast::ReturnType; + match operation { + Regular(o) => if let Some(name) = o.name { + Some(( + name, + Member::Operation(Operation { + args: o.arguments + .into_iter() + .map(|a| self.load_argument(a)) + .collect(), + return_type: match o.return_type { + ReturnType::NonVoid(t) => Some(self.load_type(*t)), + ReturnType::Void => None, + }, + doc_comment: String::new(), + }), + )) + } else { + None + }, + _ => None, + } + } + + fn load_mixin_member(&mut self, member: ast::MixinMember) -> Option<(String, Member)> { + use self::ast::MixinMember::*; + match member { + Const(c) => self.load_const(c), + Attribute(a) => self.load_attribute(a), + Operation(o) => self.load_operation(o), + } + } + + fn load_mixin(&mut self, mixin: ast::NonPartialMixin) { + let mut members = BTreeMap::new(); + for (name, member) in mixin + .members + .into_iter() + .flat_map(|m| self.load_mixin_member(m)) + { + multimap_insert(&mut members, name, member); + } + + self.load_named_type(&mixin.name, NamedType::Mixin(Mixin { members })); + } + + fn load_interface_member(&mut self, member: ast::InterfaceMember) -> Option<(String, Member)> { + use self::ast::InterfaceMember::*; + match member { + Const(c) => self.load_const(c), + Attribute(a) => self.load_attribute(a), + Operation(o) => self.load_operation(o), + _ => None, + } + } + + fn load_interface(&mut self, interface: ast::NonPartialInterface) { + fn has_attr(attrs: &Vec>, name: &str) -> bool { + use self::ast::ExtendedAttribute::*; + for attr in attrs { + if let NoArguments(ref other) = **attr { + if let &ast::Other::Identifier(ref n) = other { + return n == name; + } + } + } + false + } + + let mut members = BTreeMap::new(); + for (name, member) in interface + .members + .into_iter() + .flat_map(|m| self.load_interface_member(m)) + { + multimap_insert(&mut members, name, member); + } + + let mut result = Interface { + inherits: interface.inherits, + mixins: BTreeSet::new(), + members, + is_hidden: false, + has_class: !has_attr(&interface.extended_attributes, "NoInterfaceObject"), + rendering_context: None, + doc_comment: String::new(), + }; + + for &(context_id, context_interface) in RENDERING_CONTEXTS { + if context_interface == interface.name { + result.rendering_context = Some(context_id); + break; + } + } + + match self.types.entry(interface.name) { + Entry::Vacant(v) => { + v.insert(NamedType::Interface(result)); + } + Entry::Occupied(o) => { + assert!( + result.members.is_empty(), + "Duplicate interface: {}", + o.key() + ); + } + } + } + + fn load_includes(&mut self, includes: ast::Includes) { + if let Some(interface) = self.types + .get_mut(&includes.includer) + .and_then(NamedType::as_interface_mut) + { + interface.mixins.insert(includes.includee); + } + } + + // Load a type kind into the registry under a given name, and return + // a reference to it. + fn load_named_type(&mut self, name: &str, named_type: NamedType) -> Type { + if !self.types.contains_key(name) { + self.types.insert(name.into(), named_type); + } + name.into() + } + + // Convert an AST type kind into a type kind the generator can understand + fn load_type_kind(&mut self, kind: ast::TypeKind) -> TypeKind { + use self::ast::TypeKind::*; + + match kind { + // Primitives + Boolean => TypeKind::Primitive(Primitive::Bool), + Byte => TypeKind::Primitive(Primitive::I8), + Octet => TypeKind::Primitive(Primitive::U8), + SignedShort => TypeKind::Primitive(Primitive::I16), + UnsignedShort => TypeKind::Primitive(Primitive::U16), + SignedLong => TypeKind::Primitive(Primitive::I32), + UnsignedLong => TypeKind::Primitive(Primitive::U32), + SignedLongLong => TypeKind::Primitive(Primitive::I64), + UnsignedLongLong => TypeKind::Primitive(Primitive::U64), + RestrictedFloat | UnrestrictedFloat => TypeKind::Primitive(Primitive::F32), + RestrictedDouble | UnrestrictedDouble => TypeKind::Primitive(Primitive::F64), + + // Strings + DOMString | USVString => TypeKind::String, + ByteString => unimplemented!(), + + // TypedArrays + Int8Array => TypeKind::TypedArray(Primitive::I8), + Uint8Array => TypeKind::TypedArray(Primitive::U8), + Int16Array => TypeKind::TypedArray(Primitive::I16), + Uint16Array => TypeKind::TypedArray(Primitive::U16), + Int32Array => TypeKind::TypedArray(Primitive::I32), + Uint32Array => TypeKind::TypedArray(Primitive::U32), + Float32Array => TypeKind::TypedArray(Primitive::F32), + Float64Array => TypeKind::TypedArray(Primitive::F64), + + // Sequence + Sequence(inner) => TypeKind::Sequence(Box::new(self.load_type(*inner))), + + // Identifier + Identifier(s) => match &*s { + "BufferSource" => TypeKind::BufferSource, + "HTMLCanvasElement" => TypeKind::CanvasElement, + "ArrayBufferView" => TypeKind::ArrayBufferView, + other => TypeKind::Named(other.into()), + }, + + // Composite + Union(inners) => { + if inners.len() <= 2 { + TypeKind::Union( + inners + .into_iter() + .map(|inner| self.load_type(*inner)) + .collect(), + ) + } else { + TypeKind::Any + } + } + + // Misc + ArrayBuffer => TypeKind::ArrayBuffer, + Object => TypeKind::Object, + _ => TypeKind::Any, + } + } + + // Convert an AST type into a type the generator can understand + fn load_type(&mut self, t: ast::Type) -> Type { + Type { + kind: self.load_type_kind(t.kind), + optional: t.nullable, + } + } + + fn load_typedef(&mut self, typedef: ast::Typedef) { + let type_ = self.load_type(*typedef.type_); + self.load_named_type(&typedef.name, NamedType::Typedef(type_)); + } + + fn load_field(&mut self, field: ast::DictionaryMember) -> (String, Field) { + let type_ = self.load_type(*field.type_); + + (field.name, Field { type_ }) + } + + fn load_dictionary(&mut self, dictionary: ast::NonPartialDictionary) { + let fields = dictionary + .members + .into_iter() + .map(|m| self.load_field(m)) + .collect(); + + match self.types.entry(dictionary.name) { + Entry::Vacant(v) => { + v.insert(NamedType::Dictionary(Dictionary { + inherits: dictionary.inherits, + fields, + is_hidden: false, + })); + } + Entry::Occupied(mut o) => { + let key = o.key().clone(); + let d = o.get_mut().as_dictionary_mut().unwrap(); + // Dictionary is being extended, so make these fields all optional + for (k, mut field) in fields { + field.type_.optional = true; + if d.fields.insert(k.clone(), field).is_some() { + panic!("Duplicate field: {}.{}", key, k); + } + } + } + } + } + + fn load_enum(&mut self, enum_: ast::Enum) { + let variants = enum_.variants.into_iter().collect(); + self.load_named_type(&enum_.name, NamedType::Enum(Enum { variants })); + } + + fn load_callback(&mut self, callback: ast::Callback) { + use self::ast::ReturnType; + let args = callback + .arguments + .into_iter() + .map(|a| self.load_argument(a)) + .collect(); + let return_type = match callback.return_type { + ReturnType::NonVoid(t) => Some(self.load_type(*t)), + ReturnType::Void => None, + }; + self.load_named_type( + &callback.name, + NamedType::Callback(Callback { args, return_type }), + ); + } + + fn load_definition(&mut self, def: ast::Definition) { + use self::ast::Definition::*; + match def { + Mixin(ast::Mixin::NonPartial(m)) => self.load_mixin(m), + Interface(ast::Interface::NonPartial(i)) => self.load_interface(i), + Includes(i) => self.load_includes(i), + Typedef(t) => self.load_typedef(t), + Dictionary(ast::Dictionary::NonPartial(d)) => self.load_dictionary(d), + Enum(e) => self.load_enum(e), + Callback(c) => self.load_callback(c), + _ => {} + } + } +} diff --git a/webgl_generator/webgl_registry/types.rs b/webgl_generator/webgl_registry/types.rs new file mode 100644 index 00000000..e6d1c08e --- /dev/null +++ b/webgl_generator/webgl_registry/types.rs @@ -0,0 +1,127 @@ +// Copyright 2015 Brendan Zabarauskas and the gl-rs developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::{NamedType, Registry}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Type { + pub kind: TypeKind, + /// Optional types are the default in WebIDL, so we give this a special + /// place in the `Type` reference. It's also convenient to be able to + /// "squash" optional flags to avoid `Option>`. + pub optional: bool, +} + +/// The different kinds of primitive types supported by WebIDL +/// These are named according to their equivalents in rust. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum Primitive { + Bool, + I8, + U8, + I16, + U16, + I32, + U32, + I64, + U64, + F32, + F64, +} + +/// The definition of a type +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum TypeKind { + // Primitive types + Primitive(Primitive), + + // "Known" types, that may deserve special handling + String, + ArrayBuffer, + ArrayBufferView, + BufferSource, + CanvasElement, + + // Collection types + TypedArray(Primitive), + Sequence(Box), + Union(Vec), + + // Named types + Named(String), + + // Misc. types + Any, + Object, +} + +impl Type { + /// Return an optional version of this type reference. + /// Returns an identical copy if the type is already optional. + pub fn optional(&self) -> Self { + Type { + kind: self.kind.clone(), + optional: true, + } + } +} + +impl<'a> From<&'a str> for Type { + /// Construct a type reference from a name + fn from(s: &'a str) -> Type { + Type { + kind: TypeKind::Named(s.into()), + optional: false, + } + } +} + +impl Primitive { + /// Get the rust name for a primitive type + pub fn name(self) -> &'static str { + use self::Primitive::*; + match self { + Bool => "bool", + I8 => "i8", + U8 => "u8", + I16 => "i16", + U16 => "u16", + I32 => "i32", + U32 => "u32", + I64 => "i64", + U64 => "u64", + F32 => "f32", + F64 => "f64", + } + } +} + +impl TypeKind { + /// Look through type aliases to find the "real" definition of a type. + /// Also returns the "original name" if applicable. + pub fn flatten<'a>(&'a self, registry: &'a Registry) -> (Option<&'a str>, &'a TypeKind) { + match self { + &TypeKind::Primitive(ref p) => (Some(p.name()), self), + &TypeKind::Named(ref s) => { + if let &NamedType::Typedef(ref t) = registry.resolve_type(s) { + if !t.optional { + return (Some(s.as_str()), t.kind.flatten(registry).1); + } + } + (Some(s.as_str()), self) + } + _ => (None, self), + } + } +} diff --git a/webgl_stdweb/Cargo.toml b/webgl_stdweb/Cargo.toml new file mode 100644 index 00000000..aada1b47 --- /dev/null +++ b/webgl_stdweb/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "webgl_stdweb" +version = "0.1.0" +authors = [ + "Diggory Blake", +] +description = "WebGL bindings (stdweb)" +license = "Apache-2.0" +build = "build.rs" +documentation = "https://docs.rs/webgl_stdweb" +homepage = "https://github.com/brendanzab/gl-rs/webgl/" +repository = "https://github.com/brendanzab/gl-rs/" +readme = "README.md" +categories = ["api-bindings", "rendering::graphics-api"] +keywords = ["webgl", "stdweb"] + +[build-dependencies] +webgl_generator = { version = "0.1.0", path = "../webgl_generator" } + +[dependencies] +stdweb-derive = "0.4.0" +stdweb = "0.4.0" +serde = "1.0.0" +serde_derive = "1.0.0" diff --git a/webgl_stdweb/README.md b/webgl_stdweb/README.md new file mode 100644 index 00000000..261a0fb7 --- /dev/null +++ b/webgl_stdweb/README.md @@ -0,0 +1,3 @@ +# webgl-stdweb + +WebGL bindings using stdweb diff --git a/webgl_stdweb/build.rs b/webgl_stdweb/build.rs new file mode 100644 index 00000000..12fd1785 --- /dev/null +++ b/webgl_stdweb/build.rs @@ -0,0 +1,29 @@ +// Copyright 2015 Brendan Zabarauskas and the gl-rs developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +extern crate webgl_generator; + +use webgl_generator::*; +use std::env; +use std::fs::File; +use std::path::*; + +fn main() { + let dest = env::var("OUT_DIR").unwrap(); + let mut file = File::create(&Path::new(&dest).join("bindings.rs")).unwrap(); + + Registry::new(Api::WebGl2, Exts::ALL) + .write_bindings(StdwebGenerator, &mut file) + .unwrap(); +} diff --git a/webgl_stdweb/src/lib.rs b/webgl_stdweb/src/lib.rs new file mode 100644 index 00000000..74cf0995 --- /dev/null +++ b/webgl_stdweb/src/lib.rs @@ -0,0 +1,26 @@ +// Copyright 2015 Brendan Zabarauskas and the gl-rs developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![allow(unused_parens, non_camel_case_types)] +#![crate_name = "webgl_stdweb"] +#![crate_type = "lib"] + +#[macro_use] +extern crate serde_derive as _serde_derive; +#[macro_use] +extern crate stdweb as _stdweb; +#[macro_use] +extern crate stdweb_derive as _stdweb_derive; + +include!(concat!(env!("OUT_DIR"), "/bindings.rs"));