mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-03-31 17:31:06 +00:00
fixed conflict
This commit is contained in:
commit
223054811d
crates
backend/src
cli-support/src
js-sys/src
macro-support/src
macro
examples/webaudio
guide/src
package.jsonsrc
tests
@ -207,6 +207,7 @@ pub struct Enum {
|
|||||||
pub name: Ident,
|
pub name: Ident,
|
||||||
pub variants: Vec<Variant>,
|
pub variants: Vec<Variant>,
|
||||||
pub comments: Vec<String>,
|
pub comments: Vec<String>,
|
||||||
|
pub hole: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||||
|
@ -1038,6 +1038,7 @@ impl<'a> ToTokens for DescribeImport<'a> {
|
|||||||
impl ToTokens for ast::Enum {
|
impl ToTokens for ast::Enum {
|
||||||
fn to_tokens(&self, into: &mut TokenStream) {
|
fn to_tokens(&self, into: &mut TokenStream) {
|
||||||
let enum_name = &self.name;
|
let enum_name = &self.name;
|
||||||
|
let hole = &self.hole;
|
||||||
let cast_clauses = self.variants.iter().map(|variant| {
|
let cast_clauses = self.variants.iter().map(|variant| {
|
||||||
let variant_name = &variant.name;
|
let variant_name = &variant.name;
|
||||||
quote! {
|
quote! {
|
||||||
@ -1061,6 +1062,7 @@ impl ToTokens for ast::Enum {
|
|||||||
type Abi = u32;
|
type Abi = u32;
|
||||||
|
|
||||||
#[allow(clippy::*)]
|
#[allow(clippy::*)]
|
||||||
|
#[inline]
|
||||||
unsafe fn from_abi(
|
unsafe fn from_abi(
|
||||||
js: u32,
|
js: u32,
|
||||||
_extra: &mut ::wasm_bindgen::convert::Stack,
|
_extra: &mut ::wasm_bindgen::convert::Stack,
|
||||||
@ -1071,11 +1073,22 @@ impl ToTokens for ast::Enum {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ::wasm_bindgen::convert::OptionFromWasmAbi for #enum_name {
|
||||||
|
#[inline]
|
||||||
|
fn is_none(val: &u32) -> bool { *val == #hole }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::wasm_bindgen::convert::OptionIntoWasmAbi for #enum_name {
|
||||||
|
#[inline]
|
||||||
|
fn none() -> Self::Abi { #hole }
|
||||||
|
}
|
||||||
|
|
||||||
impl ::wasm_bindgen::describe::WasmDescribe for #enum_name {
|
impl ::wasm_bindgen::describe::WasmDescribe for #enum_name {
|
||||||
#[allow(clippy::*)]
|
#[allow(clippy::*)]
|
||||||
fn describe() {
|
fn describe() {
|
||||||
use wasm_bindgen::describe::*;
|
use wasm_bindgen::describe::*;
|
||||||
inform(ENUM);
|
inform(ENUM);
|
||||||
|
inform(#hole);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -59,7 +59,7 @@ pub enum Descriptor {
|
|||||||
Vector(Box<Descriptor>),
|
Vector(Box<Descriptor>),
|
||||||
String,
|
String,
|
||||||
Anyref,
|
Anyref,
|
||||||
Enum,
|
Enum { hole: u32 },
|
||||||
RustStruct(String),
|
RustStruct(String),
|
||||||
Char,
|
Char,
|
||||||
Option(Box<Descriptor>),
|
Option(Box<Descriptor>),
|
||||||
@ -128,7 +128,7 @@ impl Descriptor {
|
|||||||
OPTIONAL => Descriptor::Option(Box::new(Descriptor::_decode(data))),
|
OPTIONAL => Descriptor::Option(Box::new(Descriptor::_decode(data))),
|
||||||
STRING => Descriptor::String,
|
STRING => Descriptor::String,
|
||||||
ANYREF => Descriptor::Anyref,
|
ANYREF => Descriptor::Anyref,
|
||||||
ENUM => Descriptor::Enum,
|
ENUM => Descriptor::Enum { hole: get(data) },
|
||||||
RUST_STRUCT => {
|
RUST_STRUCT => {
|
||||||
let name = (0..get(data))
|
let name = (0..get(data))
|
||||||
.map(|_| char::from_u32(get(data)).unwrap())
|
.map(|_| char::from_u32(get(data)).unwrap())
|
||||||
@ -159,7 +159,7 @@ impl Descriptor {
|
|||||||
| Descriptor::U32
|
| Descriptor::U32
|
||||||
| Descriptor::F32
|
| Descriptor::F32
|
||||||
| Descriptor::F64
|
| Descriptor::F64
|
||||||
| Descriptor::Enum => true,
|
| Descriptor::Enum { .. } => true,
|
||||||
_ => return false,
|
_ => return false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -306,6 +306,14 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
.push(format!("isLikeNone({0}) ? 0 : {0}.codePointAt(0)", name));
|
.push(format!("isLikeNone({0}) ? 0 : {0}.codePointAt(0)", name));
|
||||||
return Ok(self);
|
return Ok(self);
|
||||||
}
|
}
|
||||||
|
Descriptor::Enum { hole } => {
|
||||||
|
self.cx.expose_is_like_none();
|
||||||
|
self.js_arguments
|
||||||
|
.push((name.clone(), "number | undefined".to_string()));
|
||||||
|
self.rust_arguments
|
||||||
|
.push(format!("isLikeNone({0}) ? {1} : {0}", name, hole));
|
||||||
|
return Ok(self);
|
||||||
|
}
|
||||||
_ => bail!(
|
_ => bail!(
|
||||||
"unsupported optional argument type for calling Rust function from JS: {:?}",
|
"unsupported optional argument type for calling Rust function from JS: {:?}",
|
||||||
arg
|
arg
|
||||||
@ -609,6 +617,14 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
.to_string();
|
.to_string();
|
||||||
return Ok(self);
|
return Ok(self);
|
||||||
}
|
}
|
||||||
|
Descriptor::Enum { hole } => {
|
||||||
|
self.ret_ty = "number | undefined".to_string();
|
||||||
|
self.ret_expr = format!("
|
||||||
|
const ret = RET;
|
||||||
|
return ret === {} ? undefined : ret;
|
||||||
|
", hole);
|
||||||
|
return Ok(self);
|
||||||
|
}
|
||||||
_ => bail!(
|
_ => bail!(
|
||||||
"unsupported optional return type for calling Rust function from JS: {:?}",
|
"unsupported optional return type for calling Rust function from JS: {:?}",
|
||||||
ty
|
ty
|
||||||
@ -734,12 +750,16 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
.map(|s| format!("{}: {}", s.0, s.1))
|
.map(|s| format!("{}: {}", s.0, s.1))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", ");
|
.join(", ");
|
||||||
let mut ts = format!("{} {}({})", prefix, self.js_name, ts_args);
|
let mut ts = if prefix.is_empty() {
|
||||||
|
format!("{}({})", self.js_name, ts_args)
|
||||||
|
} else {
|
||||||
|
format!("{} {}({})", prefix, self.js_name, ts_args)
|
||||||
|
};
|
||||||
if self.constructor.is_none() {
|
if self.constructor.is_none() {
|
||||||
ts.push_str(": ");
|
ts.push_str(": ");
|
||||||
ts.push_str(&self.ret_ty);
|
ts.push_str(&self.ret_ty);
|
||||||
}
|
}
|
||||||
ts.push_str(";\n");
|
ts.push(';');
|
||||||
(js, ts, self.js_doc_comments())
|
(js, ts, self.js_doc_comments())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -845,7 +845,7 @@ impl<'a> Context<'a> {
|
|||||||
",
|
",
|
||||||
name,
|
name,
|
||||||
));
|
));
|
||||||
ts_dst.push_str("free(): void;\n");
|
ts_dst.push_str(" free(): void;");
|
||||||
dst.push_str(&class.contents);
|
dst.push_str(&class.contents);
|
||||||
ts_dst.push_str(&class.typescript);
|
ts_dst.push_str(&class.typescript);
|
||||||
dst.push_str("}\n");
|
dst.push_str("}\n");
|
||||||
@ -2399,6 +2399,8 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
.contents
|
.contents
|
||||||
.push_str(&format_doc_comments(&export.comments, Some(js_doc)));
|
.push_str(&format_doc_comments(&export.comments, Some(js_doc)));
|
||||||
|
|
||||||
|
class.typescript.push_str(" "); // Indentation
|
||||||
|
|
||||||
if export.is_constructor {
|
if export.is_constructor {
|
||||||
if class.has_constructor {
|
if class.has_constructor {
|
||||||
bail!("found duplicate constructor `{}`", export.function.name);
|
bail!("found duplicate constructor `{}`", export.function.name);
|
||||||
@ -2571,12 +2573,12 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
.typescript
|
.typescript
|
||||||
.push_str(&format!("export enum {} {{", enum_.name));
|
.push_str(&format!("export enum {} {{", enum_.name));
|
||||||
|
|
||||||
variants.clear();
|
|
||||||
for variant in enum_.variants.iter() {
|
for variant in enum_.variants.iter() {
|
||||||
variants.push_str(&format!("{},", variant.name));
|
self.cx
|
||||||
|
.typescript
|
||||||
|
.push_str(&format!("\n {},", variant.name));
|
||||||
}
|
}
|
||||||
self.cx.typescript.push_str(&variants);
|
self.cx.typescript.push_str("\n}\n");
|
||||||
self.cx.typescript.push_str("}\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_struct(&mut self, struct_: &decode::Struct) -> Result<(), Error> {
|
fn generate_struct(&mut self, struct_: &decode::Struct) -> Result<(), Error> {
|
||||||
@ -2596,7 +2598,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
.argument(&descriptor)?
|
.argument(&descriptor)?
|
||||||
.ret(&Descriptor::Unit)?;
|
.ret(&Descriptor::Unit)?;
|
||||||
ts_dst.push_str(&format!(
|
ts_dst.push_str(&format!(
|
||||||
"{}{}: {};\n",
|
"\n {}{}: {};",
|
||||||
if field.readonly { "readonly " } else { "" },
|
if field.readonly { "readonly " } else { "" },
|
||||||
field.name,
|
field.name,
|
||||||
&cx.js_arguments[0].1
|
&cx.js_arguments[0].1
|
||||||
|
@ -200,6 +200,11 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
|||||||
.push(format!("{0} === 0xFFFFFF ? undefined : {0} !== 0", abi));
|
.push(format!("{0} === 0xFFFFFF ? undefined : {0} !== 0", abi));
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
Descriptor::Enum { hole } => {
|
||||||
|
self.js_arguments
|
||||||
|
.push(format!("{0} === {1} ? undefined : {0}", abi, hole));
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
Descriptor::Char => {
|
Descriptor::Char => {
|
||||||
let value = self.shim_argument();
|
let value = self.shim_argument();
|
||||||
self.js_arguments.push(format!(
|
self.js_arguments.push(format!(
|
||||||
@ -441,6 +446,14 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
|||||||
.to_string();
|
.to_string();
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
Descriptor::Enum { hole } => {
|
||||||
|
self.cx.expose_is_like_none();
|
||||||
|
self.ret_expr = format!("
|
||||||
|
const val = JS;
|
||||||
|
return isLikeNone(val) ? {} : val;
|
||||||
|
", hole);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
_ => bail!(
|
_ => bail!(
|
||||||
"unsupported optional return type for calling JS function from Rust: {:?}",
|
"unsupported optional return type for calling JS function from Rust: {:?}",
|
||||||
ty
|
ty
|
||||||
|
@ -4841,7 +4841,7 @@ macro_rules! arrays {
|
|||||||
/// Additionally the returned object can be safely mutated but the
|
/// Additionally the returned object can be safely mutated but the
|
||||||
/// input slice isn't guaranteed to be mutable.
|
/// input slice isn't guaranteed to be mutable.
|
||||||
///
|
///
|
||||||
/// Finally, the returned objet is disconnected from the input
|
/// Finally, the returned object is disconnected from the input
|
||||||
/// slice's lifetime, so there's no guarantee that the data is read
|
/// slice's lifetime, so there's no guarantee that the data is read
|
||||||
/// at the right time.
|
/// at the right time.
|
||||||
pub unsafe fn view(rust: &[$ty]) -> $name {
|
pub unsafe fn view(rust: &[$ty]) -> $name {
|
||||||
|
@ -13,8 +13,11 @@ extern crate wasm_bindgen_shared as shared;
|
|||||||
|
|
||||||
use backend::{Diagnostic, TryToTokens};
|
use backend::{Diagnostic, TryToTokens};
|
||||||
pub use parser::BindgenAttrs;
|
pub use parser::BindgenAttrs;
|
||||||
|
use quote::ToTokens;
|
||||||
use parser::MacroParse;
|
use parser::MacroParse;
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
|
use syn::parse::{Parse, ParseStream, Result as SynResult};
|
||||||
|
use quote::TokenStreamExt;
|
||||||
|
|
||||||
mod parser;
|
mod parser;
|
||||||
|
|
||||||
@ -36,3 +39,68 @@ pub fn expand(attr: TokenStream, input: TokenStream) -> Result<TokenStream, Diag
|
|||||||
|
|
||||||
Ok(tokens)
|
Ok(tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Takes the parsed input from a `#[wasm_bindgen]` macro and returns the generated bindings
|
||||||
|
pub fn expand_class_marker(attr: TokenStream, input: TokenStream) -> Result<TokenStream, Diagnostic> {
|
||||||
|
parser::reset_attrs_used();
|
||||||
|
let mut item = syn::parse2::<syn::ImplItemMethod>(input)?;
|
||||||
|
let opts: ClassMarker = syn::parse2(attr)?;
|
||||||
|
|
||||||
|
let mut program = backend::ast::Program::default();
|
||||||
|
item.macro_parse(&mut program, (&opts.class, &opts.js_class))?;
|
||||||
|
parser::assert_all_attrs_checked(); // same as above
|
||||||
|
|
||||||
|
// This is where things are slightly different, we are being expanded in the
|
||||||
|
// context of an impl so we can't inject arbitrary item-like tokens into the
|
||||||
|
// output stream. If we were to do that then it wouldn't parse!
|
||||||
|
//
|
||||||
|
// Instead what we want to do is to generate the tokens for `program` into
|
||||||
|
// the header of the function. This'll inject some no_mangle functions and
|
||||||
|
// statics and such, and they should all be valid in the context of the
|
||||||
|
// start of a function.
|
||||||
|
//
|
||||||
|
// We manually implement `ToTokens for ImplItemMethod` here, injecting our
|
||||||
|
// program's tokens before the actual method's inner body tokens.
|
||||||
|
let mut tokens = proc_macro2::TokenStream::new();
|
||||||
|
tokens.append_all(item.attrs.iter().filter(|attr| {
|
||||||
|
match attr.style {
|
||||||
|
syn::AttrStyle::Outer => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
item.vis.to_tokens(&mut tokens);
|
||||||
|
item.sig.to_tokens(&mut tokens);
|
||||||
|
let mut err = None;
|
||||||
|
item.block.brace_token.surround(&mut tokens, |tokens| {
|
||||||
|
if let Err(e) = program.try_to_tokens(tokens) {
|
||||||
|
err = Some(e);
|
||||||
|
}
|
||||||
|
tokens.append_all(item.attrs.iter().filter(|attr| {
|
||||||
|
match attr.style {
|
||||||
|
syn::AttrStyle::Inner(_) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
tokens.append_all(&item.block.stmts);
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(err) = err {
|
||||||
|
return Err(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(tokens)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ClassMarker {
|
||||||
|
class: syn::Ident,
|
||||||
|
js_class: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for ClassMarker {
|
||||||
|
fn parse(input: ParseStream) -> SynResult<Self> {
|
||||||
|
let class = input.parse::<syn::Ident>()?;
|
||||||
|
input.parse::<Token![=]>()?;
|
||||||
|
let js_class = input.parse::<syn::LitStr>()?.value();
|
||||||
|
Ok(ClassMarker { class, js_class })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -796,7 +796,7 @@ impl<'a> MacroParse<(Option<BindgenAttrs>, &'a mut TokenStream)> for syn::Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> MacroParse<BindgenAttrs> for &'a mut syn::ItemImpl {
|
impl<'a> MacroParse<BindgenAttrs> for &'a mut syn::ItemImpl {
|
||||||
fn macro_parse(self, program: &mut ast::Program, opts: BindgenAttrs) -> Result<(), Diagnostic> {
|
fn macro_parse(self, _program: &mut ast::Program, opts: BindgenAttrs) -> Result<(), Diagnostic> {
|
||||||
if self.defaultness.is_some() {
|
if self.defaultness.is_some() {
|
||||||
bail_span!(
|
bail_span!(
|
||||||
self.defaultness,
|
self.defaultness,
|
||||||
@ -830,7 +830,7 @@ impl<'a> MacroParse<BindgenAttrs> for &'a mut syn::ItemImpl {
|
|||||||
};
|
};
|
||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
for item in self.items.iter_mut() {
|
for item in self.items.iter_mut() {
|
||||||
if let Err(e) = (&name, item).macro_parse(program, &opts) {
|
if let Err(e) = prepare_for_impl_recursion(item, &name, &opts) {
|
||||||
errors.push(e);
|
errors.push(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -840,77 +840,106 @@ impl<'a> MacroParse<BindgenAttrs> for &'a mut syn::ItemImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> MacroParse<&'a BindgenAttrs> for (&'a Ident, &'b mut syn::ImplItem) {
|
// Prepare for recursion into an `impl` block. Here we want to attach an
|
||||||
|
// internal attribute, `__wasm_bindgen_class_marker`, with any metadata we need
|
||||||
|
// to pass from the impl to the impl item. Recursive macro expansion will then
|
||||||
|
// expand the `__wasm_bindgen_class_marker` attribute.
|
||||||
|
//
|
||||||
|
// Note that we currently do this because inner items may have things like cfgs
|
||||||
|
// on them, so we want to expand the impl first, let the insides get cfg'd, and
|
||||||
|
// then go for the rest.
|
||||||
|
fn prepare_for_impl_recursion(
|
||||||
|
item: &mut syn::ImplItem,
|
||||||
|
class: &Ident,
|
||||||
|
impl_opts: &BindgenAttrs
|
||||||
|
) -> Result<(), Diagnostic> {
|
||||||
|
let method = match item {
|
||||||
|
syn::ImplItem::Method(m) => m,
|
||||||
|
syn::ImplItem::Const(_) => {
|
||||||
|
bail_span!(
|
||||||
|
&*item,
|
||||||
|
"const definitions aren't supported with #[wasm_bindgen]"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
syn::ImplItem::Type(_) => bail_span!(
|
||||||
|
&*item,
|
||||||
|
"type definitions in impls aren't supported with #[wasm_bindgen]"
|
||||||
|
),
|
||||||
|
syn::ImplItem::Existential(_) => bail_span!(
|
||||||
|
&*item,
|
||||||
|
"existentials in impls aren't supported with #[wasm_bindgen]"
|
||||||
|
),
|
||||||
|
syn::ImplItem::Macro(_) => {
|
||||||
|
// In theory we want to allow this, but we have no way of expanding
|
||||||
|
// the macro and then placing our magical attributes on the expanded
|
||||||
|
// functions. As a result, just disallow it for now to hopefully
|
||||||
|
// ward off buggy results from this macro.
|
||||||
|
bail_span!(&*item, "macros in impls aren't supported");
|
||||||
|
}
|
||||||
|
syn::ImplItem::Verbatim(_) => panic!("unparsed impl item?"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let js_class = impl_opts
|
||||||
|
.js_class()
|
||||||
|
.map(|s| s.0.to_string())
|
||||||
|
.unwrap_or(class.to_string());
|
||||||
|
|
||||||
|
method.attrs.insert(0, syn::Attribute {
|
||||||
|
pound_token: Default::default(),
|
||||||
|
style: syn::AttrStyle::Outer,
|
||||||
|
bracket_token: Default::default(),
|
||||||
|
path: syn::Ident::new("__wasm_bindgen_class_marker", Span::call_site()).into(),
|
||||||
|
tts: quote::quote! { (#class = #js_class) }.into(),
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> MacroParse<(&'a Ident, &'a str)> for &'b mut syn::ImplItemMethod {
|
||||||
fn macro_parse(
|
fn macro_parse(
|
||||||
self,
|
self,
|
||||||
program: &mut ast::Program,
|
program: &mut ast::Program,
|
||||||
impl_opts: &'a BindgenAttrs,
|
(class, js_class): (&'a Ident, &'a str),
|
||||||
) -> Result<(), Diagnostic> {
|
) -> Result<(), Diagnostic> {
|
||||||
let (class, item) = self;
|
match self.vis {
|
||||||
let method = match item {
|
|
||||||
syn::ImplItem::Method(ref mut m) => m,
|
|
||||||
syn::ImplItem::Const(_) => {
|
|
||||||
bail_span!(
|
|
||||||
&*item,
|
|
||||||
"const definitions aren't supported with #[wasm_bindgen]"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
syn::ImplItem::Type(_) => bail_span!(
|
|
||||||
&*item,
|
|
||||||
"type definitions in impls aren't supported with #[wasm_bindgen]"
|
|
||||||
),
|
|
||||||
syn::ImplItem::Existential(_) => bail_span!(
|
|
||||||
&*item,
|
|
||||||
"existentials in impls aren't supported with #[wasm_bindgen]"
|
|
||||||
),
|
|
||||||
syn::ImplItem::Macro(_) => {
|
|
||||||
bail_span!(&*item, "macros in impls aren't supported");
|
|
||||||
}
|
|
||||||
syn::ImplItem::Verbatim(_) => panic!("unparsed impl item?"),
|
|
||||||
};
|
|
||||||
match method.vis {
|
|
||||||
syn::Visibility::Public(_) => {}
|
syn::Visibility::Public(_) => {}
|
||||||
_ => return Ok(()),
|
_ => return Ok(()),
|
||||||
}
|
}
|
||||||
if method.defaultness.is_some() {
|
if self.defaultness.is_some() {
|
||||||
panic!("default methods are not supported");
|
panic!("default methods are not supported");
|
||||||
}
|
}
|
||||||
if method.sig.constness.is_some() {
|
if self.sig.constness.is_some() {
|
||||||
bail_span!(
|
bail_span!(
|
||||||
method.sig.constness,
|
self.sig.constness,
|
||||||
"can only #[wasm_bindgen] non-const functions",
|
"can only #[wasm_bindgen] non-const functions",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if method.sig.unsafety.is_some() {
|
if self.sig.unsafety.is_some() {
|
||||||
bail_span!(method.sig.unsafety, "can only bindgen safe functions",);
|
bail_span!(self.sig.unsafety, "can only bindgen safe functions",);
|
||||||
}
|
}
|
||||||
|
|
||||||
let opts = BindgenAttrs::find(&mut method.attrs)?;
|
let opts = BindgenAttrs::find(&mut self.attrs)?;
|
||||||
let comments = extract_doc_comments(&method.attrs);
|
let comments = extract_doc_comments(&self.attrs);
|
||||||
let is_constructor = opts.constructor().is_some();
|
let is_constructor = opts.constructor().is_some();
|
||||||
let (function, method_self) = function_from_decl(
|
let (function, method_self) = function_from_decl(
|
||||||
&method.sig.ident,
|
&self.sig.ident,
|
||||||
&opts,
|
&opts,
|
||||||
Box::new(method.sig.decl.clone()),
|
Box::new(self.sig.decl.clone()),
|
||||||
method.attrs.clone(),
|
self.attrs.clone(),
|
||||||
method.vis.clone(),
|
self.vis.clone(),
|
||||||
true,
|
true,
|
||||||
Some(class),
|
Some(class),
|
||||||
)?;
|
)?;
|
||||||
let js_class = impl_opts
|
|
||||||
.js_class()
|
|
||||||
.map(|s| s.0.to_string())
|
|
||||||
.unwrap_or(class.to_string());
|
|
||||||
|
|
||||||
program.exports.push(ast::Export {
|
program.exports.push(ast::Export {
|
||||||
rust_class: Some(class.clone()),
|
rust_class: Some(class.clone()),
|
||||||
js_class: Some(js_class),
|
js_class: Some(js_class.to_string()),
|
||||||
method_self,
|
method_self,
|
||||||
is_constructor,
|
is_constructor,
|
||||||
function,
|
function,
|
||||||
comments,
|
comments,
|
||||||
start: false,
|
start: false,
|
||||||
rust_name: method.sig.ident.clone(),
|
rust_name: self.sig.ident.clone(),
|
||||||
});
|
});
|
||||||
opts.check_used()?;
|
opts.check_used()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -924,6 +953,12 @@ impl MacroParse<()> for syn::ItemEnum {
|
|||||||
_ => bail_span!(self, "only public enums are allowed with #[wasm_bindgen]"),
|
_ => bail_span!(self, "only public enums are allowed with #[wasm_bindgen]"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.variants.len() == 0 {
|
||||||
|
bail_span!(self, "cannot export empty enums to JS");
|
||||||
|
}
|
||||||
|
|
||||||
|
let has_discriminant = self.variants[0].discriminant.is_some();
|
||||||
|
|
||||||
let variants = self
|
let variants = self
|
||||||
.variants
|
.variants
|
||||||
.iter()
|
.iter()
|
||||||
@ -933,6 +968,14 @@ impl MacroParse<()> for syn::ItemEnum {
|
|||||||
syn::Fields::Unit => (),
|
syn::Fields::Unit => (),
|
||||||
_ => bail_span!(v.fields, "only C-Style enums allowed with #[wasm_bindgen]"),
|
_ => bail_span!(v.fields, "only C-Style enums allowed with #[wasm_bindgen]"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Require that everything either has a discriminant or doesn't.
|
||||||
|
// We don't really want to get in the business of emulating how
|
||||||
|
// rustc assigns values to enums.
|
||||||
|
if v.discriminant.is_some() != has_discriminant {
|
||||||
|
bail_span!(v, "must either annotate discriminant of all variants or none");
|
||||||
|
}
|
||||||
|
|
||||||
let value = match v.discriminant {
|
let value = match v.discriminant {
|
||||||
Some((
|
Some((
|
||||||
_,
|
_,
|
||||||
@ -963,12 +1006,30 @@ impl MacroParse<()> for syn::ItemEnum {
|
|||||||
value,
|
value,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<Result<_, Diagnostic>>()?;
|
.collect::<Result<Vec<_>, Diagnostic>>()?;
|
||||||
|
|
||||||
|
let mut values = variants.iter().map(|v| v.value).collect::<Vec<_>>();
|
||||||
|
values.sort();
|
||||||
|
let hole = values.windows(2)
|
||||||
|
.filter_map(|window| {
|
||||||
|
if window[0] + 1 != window[1] {
|
||||||
|
Some(window[0] + 1)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.next()
|
||||||
|
.unwrap_or(*values.last().unwrap() + 1);
|
||||||
|
for value in values {
|
||||||
|
assert!(hole != value);
|
||||||
|
}
|
||||||
|
|
||||||
let comments = extract_doc_comments(&self.attrs);
|
let comments = extract_doc_comments(&self.attrs);
|
||||||
program.enums.push(ast::Enum {
|
program.enums.push(ast::Enum {
|
||||||
name: self.ident,
|
name: self.ident,
|
||||||
variants,
|
variants,
|
||||||
comments,
|
comments,
|
||||||
|
hole,
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -19,3 +19,16 @@ pub fn wasm_bindgen(attr: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
Err(diagnostic) => (quote! { #diagnostic }).into(),
|
Err(diagnostic) => (quote! { #diagnostic }).into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn __wasm_bindgen_class_marker(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
|
match macro_support::expand_class_marker(attr.into(), input.into()) {
|
||||||
|
Ok(tokens) => {
|
||||||
|
if cfg!(feature = "xxx_debug_only_print_generated_code") {
|
||||||
|
println!("{}", tokens);
|
||||||
|
}
|
||||||
|
tokens.into()
|
||||||
|
}
|
||||||
|
Err(diagnostic) => (quote! { #diagnostic }).into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -36,8 +36,14 @@ impl A {
|
|||||||
x!();
|
x!();
|
||||||
|
|
||||||
// pub default fn foo() {} // TODO: compiler's pretty printer totally broken
|
// pub default fn foo() {} // TODO: compiler's pretty printer totally broken
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
impl A {
|
||||||
pub const fn foo() {}
|
pub const fn foo() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
impl A {
|
||||||
pub unsafe fn foo() {}
|
pub unsafe fn foo() {}
|
||||||
}
|
}
|
||||||
|
@ -47,15 +47,15 @@ error: macros in impls aren't supported
|
|||||||
| ^^^^^
|
| ^^^^^
|
||||||
|
|
||||||
error: can only #[wasm_bindgen] non-const functions
|
error: can only #[wasm_bindgen] non-const functions
|
||||||
--> $DIR/invalid-methods.rs:41:9
|
--> $DIR/invalid-methods.rs:43:9
|
||||||
|
|
|
|
||||||
41 | pub const fn foo() {}
|
43 | pub const fn foo() {}
|
||||||
| ^^^^^
|
| ^^^^^
|
||||||
|
|
||||||
error: can only bindgen safe functions
|
error: can only bindgen safe functions
|
||||||
--> $DIR/invalid-methods.rs:42:9
|
--> $DIR/invalid-methods.rs:48:9
|
||||||
|
|
|
|
||||||
42 | pub unsafe fn foo() {}
|
48 | pub unsafe fn foo() {}
|
||||||
| ^^^^^^
|
| ^^^^^^
|
||||||
|
|
||||||
error: aborting due to 10 previous errors
|
error: aborting due to 10 previous errors
|
||||||
|
@ -4,6 +4,8 @@ extern crate wasm_bindgen;
|
|||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
struct A;
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
impl A {
|
impl A {
|
||||||
#[wasm_bindgen(method)]
|
#[wasm_bindgen(method)]
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
error: unused #[wasm_bindgen] attribute
|
error: unused #[wasm_bindgen] attribute
|
||||||
--> $DIR/unused-attributes.rs:9:20
|
--> $DIR/unused-attributes.rs:11:20
|
||||||
|
|
|
|
||||||
9 | #[wasm_bindgen(method)]
|
11 | #[wasm_bindgen(method)]
|
||||||
| ^^^^^^
|
| ^^^^^^
|
||||||
|
|
||||||
error: unused #[wasm_bindgen] attribute
|
error: unused #[wasm_bindgen] attribute
|
||||||
--> $DIR/unused-attributes.rs:10:20
|
--> $DIR/unused-attributes.rs:12:20
|
||||||
|
|
|
|
||||||
10 | #[wasm_bindgen(method)]
|
12 | #[wasm_bindgen(method)]
|
||||||
| ^^^^^^
|
| ^^^^^^
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error: aborting due to 2 previous errors
|
||||||
|
@ -19,21 +19,21 @@ import('./pkg/webaudio')
|
|||||||
const primary_slider = document.getElementById("primary_input");
|
const primary_slider = document.getElementById("primary_input");
|
||||||
primary_slider.addEventListener("input", event => {
|
primary_slider.addEventListener("input", event => {
|
||||||
if (fm) {
|
if (fm) {
|
||||||
fm.set_note(event.target.value);
|
fm.set_note(parseInt(event.target.value));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const fm_freq = document.getElementById("fm_freq");
|
const fm_freq = document.getElementById("fm_freq");
|
||||||
fm_freq.addEventListener("input", event => {
|
fm_freq.addEventListener("input", event => {
|
||||||
if (fm) {
|
if (fm) {
|
||||||
fm.set_fm_frequency(event.target.value);
|
fm.set_fm_frequency(parseFloat(event.target.value));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const fm_amount = document.getElementById("fm_amount");
|
const fm_amount = document.getElementById("fm_amount");
|
||||||
fm_amount.addEventListener("input", event => {
|
fm_amount.addEventListener("input", event => {
|
||||||
if (fm) {
|
if (fm) {
|
||||||
fm.set_fm_amount(event.target.value);
|
fm.set_fm_amount(parseFloat(event.target.value));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
@ -44,6 +44,10 @@ also like to be aware of it!
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Warning:** doing this implies the polyfill will always be used,
|
||||||
|
even if native APIs are available. This has a very significant
|
||||||
|
performance impact (the polyfill was measured to be 100x slower in Chromium)!
|
||||||
|
|
||||||
2. If you're not using a bundler you can also include support manually by
|
2. If you're not using a bundler you can also include support manually by
|
||||||
adding a `<script>` tag which defines the `TextEncoder` and `TextDecoder`
|
adding a `<script>` tag which defines the `TextEncoder` and `TextDecoder`
|
||||||
globals. [This StackOverflow question][soq] has some example usage and MDN
|
globals. [This StackOverflow question][soq] has some example usage and MDN
|
||||||
|
@ -24,7 +24,7 @@ install:
|
|||||||
- tar xf wasm-bindgen-0.2.21-x86_64-unknown-linux-musl.tar.gz
|
- tar xf wasm-bindgen-0.2.21-x86_64-unknown-linux-musl.tar.gz
|
||||||
- chmod +x wasm-bindgen-0.2.21-x86_64-unknown-linux-musl/wasm-bindgen
|
- chmod +x wasm-bindgen-0.2.21-x86_64-unknown-linux-musl/wasm-bindgen
|
||||||
# Moves the binaries to a directory that is in your PATH
|
# Moves the binaries to a directory that is in your PATH
|
||||||
- mv wasm-bindgen-0.2.19-x86_64-unknown-linux-musl/wasm-bindgen* ~/.cargo/bin
|
- mv wasm-bindgen-0.2.21-x86_64-unknown-linux-musl/wasm-bindgen* ~/.cargo/bin
|
||||||
# Install node.js with nvm.
|
# Install node.js with nvm.
|
||||||
- curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash
|
- curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash
|
||||||
- source ~/.nvm/nvm.sh
|
- source ~/.nvm/nvm.sh
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
"serve": "webpack-dev-server -p"
|
"serve": "webpack-dev-server -p"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@wasm-tool/wasm-pack-plugin": "0.2.1",
|
"@wasm-tool/wasm-pack-plugin": "0.2.3",
|
||||||
"text-encoding": "^0.7.0",
|
"text-encoding": "^0.7.0",
|
||||||
"html-webpack-plugin": "^3.2.0",
|
"html-webpack-plugin": "^3.2.0",
|
||||||
"webpack": "^4.11.1",
|
"webpack": "^4.11.1",
|
||||||
|
72
src/lib.rs
72
src/lib.rs
@ -36,8 +36,11 @@ macro_rules! if_std {
|
|||||||
/// use wasm_bindgen::prelude::*;
|
/// use wasm_bindgen::prelude::*;
|
||||||
/// ```
|
/// ```
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use wasm_bindgen_macro::__wasm_bindgen_class_marker;
|
||||||
pub use wasm_bindgen_macro::wasm_bindgen;
|
pub use wasm_bindgen_macro::wasm_bindgen;
|
||||||
pub use JsValue;
|
pub use JsValue;
|
||||||
|
pub use UnwrapThrowExt;
|
||||||
|
|
||||||
if_std! {
|
if_std! {
|
||||||
pub use closure::Closure;
|
pub use closure::Closure;
|
||||||
@ -638,6 +641,75 @@ pub fn throw_val(s: JsValue) -> ! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An extension trait for `Option<T>` and `Result<T, E>` for unwraping the `T`
|
||||||
|
/// value, or throwing a JS error if it is not available.
|
||||||
|
///
|
||||||
|
/// These methods should have a smaller code size footprint than the normal
|
||||||
|
/// `Option::unwrap` and `Option::expect` methods, but they are specific to
|
||||||
|
/// working with wasm and JS.
|
||||||
|
///
|
||||||
|
/// On non-wasm32 targets, defaults to the normal unwrap/expect calls.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// // If the value is `Option::Some` or `Result::Ok`, then we just get the
|
||||||
|
/// // contained `T` value.
|
||||||
|
/// let x = Some(42);
|
||||||
|
/// assert_eq!(x.unwrap_throw(), 42);
|
||||||
|
///
|
||||||
|
/// let y: Option<i32> = None;
|
||||||
|
///
|
||||||
|
/// // This call would throw an error to JS!
|
||||||
|
/// //
|
||||||
|
/// // y.unwrap_throw()
|
||||||
|
/// //
|
||||||
|
/// // And this call would throw an error to JS with a custom error message!
|
||||||
|
/// //
|
||||||
|
/// // y.expect_throw("woopsie daisy!")
|
||||||
|
/// ```
|
||||||
|
pub trait UnwrapThrowExt<T>: Sized {
|
||||||
|
/// Unwrap this `Option` or `Result`, but instead of panicking on failure,
|
||||||
|
/// throw an exception to JavaScript.
|
||||||
|
fn unwrap_throw(self) -> T {
|
||||||
|
self.expect_throw("`unwrap_throw` failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unwrap this container's `T` value, or throw an error to JS with the
|
||||||
|
/// given message if the `T` value is unavailable (e.g. an `Option<T>` is
|
||||||
|
/// `None`).
|
||||||
|
fn expect_throw(self, message: &str) -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> UnwrapThrowExt<T> for Option<T> {
|
||||||
|
fn expect_throw(self, message: &str) -> T {
|
||||||
|
if cfg!(all(target_arch = "wasm32", not(target_os = "emscripten"))) {
|
||||||
|
match self {
|
||||||
|
Some(val) => val,
|
||||||
|
None => throw_str(message),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.expect(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E> UnwrapThrowExt<T> for Result<T, E>
|
||||||
|
where
|
||||||
|
E: core::fmt::Debug,
|
||||||
|
{
|
||||||
|
fn expect_throw(self, message: &str) -> T {
|
||||||
|
if cfg!(all(target_arch = "wasm32", not(target_os = "emscripten"))) {
|
||||||
|
match self {
|
||||||
|
Ok(val) => val,
|
||||||
|
Err(_) => throw_str(message),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.expect(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a handle to this wasm instance's `WebAssembly.Module`
|
/// Returns a handle to this wasm instance's `WebAssembly.Module`
|
||||||
///
|
///
|
||||||
/// Note that this is only available when the final wasm app is built with
|
/// Note that this is only available when the final wasm app is built with
|
||||||
|
23
tests/unwrap_throw.rs
Normal file
23
tests/unwrap_throw.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
extern crate wasm_bindgen;
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unwrap_throw_ok() {
|
||||||
|
assert_eq!(Some(42).unwrap_throw(), 42);
|
||||||
|
let x: Result<i32, ()> = Ok(42);
|
||||||
|
assert_eq!(x.unwrap_throw(), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn unwrap_throw_none() {
|
||||||
|
let x: Option<i32> = None;
|
||||||
|
x.unwrap_throw();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn unwrap_throw_err() {
|
||||||
|
let x: Result<i32, ()> = Err(());
|
||||||
|
x.unwrap_throw();
|
||||||
|
}
|
@ -143,3 +143,8 @@ exports.js_renamed_export = () => {
|
|||||||
x.foo();
|
x.foo();
|
||||||
x.bar(x);
|
x.bar(x);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.js_conditional_bindings = () => {
|
||||||
|
const x = new wasm.ConditionalBindings();
|
||||||
|
x.free();
|
||||||
|
};
|
||||||
|
@ -22,6 +22,7 @@ extern "C" {
|
|||||||
fn js_js_rename();
|
fn js_js_rename();
|
||||||
fn js_access_fields();
|
fn js_access_fields();
|
||||||
fn js_renamed_export();
|
fn js_renamed_export();
|
||||||
|
fn js_conditional_bindings();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen_test]
|
#[wasm_bindgen_test]
|
||||||
@ -402,3 +403,19 @@ impl RenamedExport {
|
|||||||
fn renamed_export() {
|
fn renamed_export() {
|
||||||
js_renamed_export();
|
js_renamed_export();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
|
||||||
|
pub struct ConditionalBindings {
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
|
||||||
|
impl ConditionalBindings {
|
||||||
|
#[cfg_attr(target_arch = "wasm32", wasm_bindgen(constructor))]
|
||||||
|
pub fn new() -> ConditionalBindings {
|
||||||
|
ConditionalBindings {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[wasm_bindgen_test]
|
||||||
|
fn conditional_bindings() {
|
||||||
|
js_conditional_bindings();
|
||||||
|
}
|
||||||
|
@ -18,3 +18,13 @@ exports.js_c_style_enum_with_custom_values = () => {
|
|||||||
|
|
||||||
assert.strictEqual(wasm.enum_with_custom_values_cycle(wasm.ColorWithCustomValues.Green), wasm.ColorWithCustomValues.Yellow);
|
assert.strictEqual(wasm.enum_with_custom_values_cycle(wasm.ColorWithCustomValues.Green), wasm.ColorWithCustomValues.Yellow);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.js_handle_optional_enums = x => wasm.handle_optional_enums(x);
|
||||||
|
|
||||||
|
exports.js_expect_enum = (a, b) => {
|
||||||
|
assert.strictEqual(a, b);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.js_expect_enum_none = a => {
|
||||||
|
assert.strictEqual(a, undefined);
|
||||||
|
};
|
||||||
|
@ -6,9 +6,13 @@ use wasm_bindgen_test::*;
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
fn js_c_style_enum();
|
fn js_c_style_enum();
|
||||||
fn js_c_style_enum_with_custom_values();
|
fn js_c_style_enum_with_custom_values();
|
||||||
|
fn js_handle_optional_enums(x: Option<Color>) -> Option<Color>;
|
||||||
|
fn js_expect_enum(x: Color, y: Option<Color>);
|
||||||
|
fn js_expect_enum_none(x: Option<Color>);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
pub enum Color {
|
pub enum Color {
|
||||||
Green,
|
Green,
|
||||||
Yellow,
|
Yellow,
|
||||||
@ -22,7 +26,7 @@ pub mod inner {
|
|||||||
pub enum ColorWithCustomValues {
|
pub enum ColorWithCustomValues {
|
||||||
Green = 21,
|
Green = 21,
|
||||||
Yellow = 34,
|
Yellow = 34,
|
||||||
Red,
|
Red = 2,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,3 +57,28 @@ fn c_style_enum() {
|
|||||||
fn c_style_enum_with_custom_values() {
|
fn c_style_enum_with_custom_values() {
|
||||||
js_c_style_enum_with_custom_values();
|
js_c_style_enum_with_custom_values();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn handle_optional_enums(x: Option<Color>) -> Option<Color> {
|
||||||
|
x
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen_test]
|
||||||
|
fn test_optional_enums() {
|
||||||
|
use self::Color::*;
|
||||||
|
|
||||||
|
assert_eq!(js_handle_optional_enums(None), None);
|
||||||
|
assert_eq!(js_handle_optional_enums(Some(Green)), Some(Green));
|
||||||
|
assert_eq!(js_handle_optional_enums(Some(Yellow)), Some(Yellow));
|
||||||
|
assert_eq!(js_handle_optional_enums(Some(Red)), Some(Red));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen_test]
|
||||||
|
fn test_optional_enum_values() {
|
||||||
|
use self::Color::*;
|
||||||
|
|
||||||
|
js_expect_enum(Green, Some(Green));
|
||||||
|
js_expect_enum(Yellow, Some(Yellow));
|
||||||
|
js_expect_enum(Red, Some(Red));
|
||||||
|
js_expect_enum_none(None);
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user