Skip to content

Commit c5d3ca9

Browse files
authored
Merge pull request #862 from alexcrichton/more-types
Enable union types without interfaces in WebIDL
2 parents 9a1fa5a + d098d3c commit c5d3ca9

File tree

1 file changed

+45
-14
lines changed

1 file changed

+45
-14
lines changed

crates/webidl/src/idl_type.rs

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -517,27 +517,58 @@ impl<'a> IdlType<'a> {
517517
| IdlType::Dictionary(name)
518518
| IdlType::CallbackInterface { name, .. } => {
519519
let ty = ident_ty(rust_ident(camel_case_ident(name).as_str()));
520-
if pos == TypePosition::Argument {
521-
Some(shared_ref(ty, false))
522-
} else {
523-
Some(ty)
524-
}
520+
anyref(ty)
525521
},
526522
IdlType::Enum(name) => Some(ident_ty(rust_ident(camel_case_ident(name).as_str()))),
527523

528-
IdlType::Nullable(idl_type) => Some(option_ty(idl_type.to_syn_type(pos)?)),
524+
IdlType::Nullable(idl_type) => {
525+
let inner = idl_type.to_syn_type(pos)?;
526+
527+
// TODO: this is a bit of a hack, but `Option<JsValue>` isn't
528+
// supported right now. As a result if we see `JsValue` for our
529+
// inner type, leave that as the same when we create a nullable
530+
// version of that. That way `any?` just becomes `JsValue` and
531+
// it's up to users to dispatch and/or create instances
532+
// appropriately.
533+
if let syn::Type::Path(path) = &inner {
534+
if path.qself.is_none() &&
535+
path.path.segments.last().map(|p| p.value().ident == "JsValue")
536+
.unwrap_or(false)
537+
{
538+
return Some(inner.clone())
539+
}
540+
}
541+
542+
Some(option_ty(inner))
543+
}
529544
IdlType::FrozenArray(_idl_type) => None,
530545
IdlType::Sequence(_idl_type) => None,
531546
IdlType::Promise(_idl_type) => js_sys("Promise"),
532547
IdlType::Record(_idl_type_from, _idl_type_to) => None,
533548
IdlType::Union(idl_types) => {
534-
// Handles union types in all places except operation argument types.
535-
// Currently treats them as object type, if possible.
536-
// TODO: add better support for union types here?
537-
// Approaches for it:
538-
// 1. Use strategy of finding the nearest common subclass (finding the best type
539-
// that is suitable for all values of this union)
540-
// 2. Generate enum with payload in Rust for each union type
549+
// Note that most union types have already been expanded to
550+
// their components via `flatten`. Unions in a return position
551+
// or dictionary fields, however, haven't been flattened, which
552+
// means we may need to conver them to a `syn` type.
553+
//
554+
// Currently this does a bit of a "poor man's" tree traversal by
555+
// saying that if all union members are interfaces we can assume
556+
// they've all got `Object` as a superclass, so we can take an
557+
// object here. If any are not an interface though we
558+
// pessimisitcally translate the union into a `JsValue`,
559+
// absolutely anything. It's up to the application to figure out
560+
// what to do with that.
561+
//
562+
// TODO: we should probably do a better job here translating
563+
// unions to a single type. Two possible strategies could be:
564+
//
565+
// 1. Use strategy of finding the nearest common subclass
566+
// (finding the best type that is suitable for all values of
567+
// this union) instead of always assuming `Object`.
568+
// 2. Generate enum with payload in Rust for each union type.
569+
// Such an enum, however, might have a relatively high
570+
// overhead in creating it from a JS value, but would be
571+
// cheap to convert from a variant back to a JS value.
541572
if idl_types
542573
.iter()
543574
.all(|idl_type|
@@ -548,7 +579,7 @@ impl<'a> IdlType<'a> {
548579
) {
549580
IdlType::Object.to_syn_type(pos)
550581
} else {
551-
None
582+
IdlType::Any.to_syn_type(pos)
552583
}
553584
},
554585

0 commit comments

Comments
 (0)