From e0301f124e3138902e8ea1a0df646ee9a4975d32 Mon Sep 17 00:00:00 2001 From: ldm0 Date: Sun, 11 Oct 2026 21:59:37 +0800 Subject: [PATCH] feat(webidl): derive sequences of dictionary values Extracted from wpt-misc-fix. Source-commit: 4fb35a340e3dd773d2f805c5c4e6bed2e8e71b69 --- moli-webidl-derive/src/converter.rs | 1 + moli-webidl-derive/src/lib.rs | 1 + moli-webidl/tests/dictionary_sequence.rs | 54 ++++++++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 moli-webidl/tests/dictionary_sequence.rs diff --git a/moli-webidl-derive/src/converter.rs b/moli-webidl-derive/src/converter.rs index 2f926bbb27..3498440c09 100644 --- a/moli-webidl-derive/src/converter.rs +++ b/moli-webidl-derive/src/converter.rs @@ -66,6 +66,7 @@ impl ConverterKind { "unrestricted_double" => Ok(Self::UnrestrictedDouble), "boolean" => Ok(Self::Boolean), "enum" => Ok(Self::Enum), + "dictionary" => Ok(Self::Dictionary), "raw" => Ok(Self::Raw), _ => Err(Error::new(value.span(), "unsupported converter kind")), } diff --git a/moli-webidl-derive/src/lib.rs b/moli-webidl-derive/src/lib.rs index e9f7e0ccb6..8f82373567 100644 --- a/moli-webidl-derive/src/lib.rs +++ b/moli-webidl-derive/src/lib.rs @@ -20,6 +20,7 @@ use syn::{DeriveInput, parse_macro_input}; /// values parse an empty dictionary; optional fields skip missing/undefined. /// `#[webidl(sequence)]` converts one iterable to `Vec`, using inferred or /// explicit item conversion, including `interface` and `brand_check` metadata. +/// `sequence, converter = "dictionary"` converts each item as a dictionary. #[proc_macro_derive(WebIdlArgs, attributes(webidl))] pub fn derive_webidl_args(input: TokenStream) -> TokenStream { match expand::expand_webidl_args(parse_macro_input!(input as DeriveInput)) { diff --git a/moli-webidl/tests/dictionary_sequence.rs b/moli-webidl/tests/dictionary_sequence.rs new file mode 100644 index 0000000000..0a4c273c92 --- /dev/null +++ b/moli-webidl/tests/dictionary_sequence.rs @@ -0,0 +1,54 @@ +use moli_webidl::{Context, WebIdlDictionary}; + +#[derive(WebIdlDictionary)] +#[webidl(prefix = "Plane")] +struct Plane { + #[webidl(required)] + offset: u32, +} +#[derive(WebIdlDictionary)] +#[webidl(prefix = "Planes")] +struct Planes { + #[webidl(sequence, converter = "dictionary")] + layout: Option>, +} +#[test] +fn sequence_dictionary_conversion_observes_iteration_and_required_members() { + moli_v8_test_util::ensure_v8(); + let mut isolate = v8::Isolate::new(Default::default()); + v8::scope!(let scope, &mut isolate); + let context = v8::Context::new(scope, Default::default()); + let scope = &mut v8::ContextScope::new(scope, context); + for (source, expected) in [ + ("({layout:[{offset:3},{offset:7}]})", Some(vec![3, 7])), + ("({layout:[]})", Some(vec![])), + ("({})", None), + ] { + let source = v8::String::new(scope, source).unwrap(); + let value = v8::Script::compile(scope, source, None) + .unwrap() + .run(scope) + .unwrap(); + let planes = + moli_webidl::parse_dictionary::(scope, value, Context::argument("Test", 1)) + .unwrap() + .unwrap(); + assert_eq!( + planes + .layout + .map(|list| list.into_iter().map(|p| p.offset).collect::>()), + expected + ); + } + for source in ["({layout:[null]})", "({layout:[{}]})", "({layout:{}})"] { + let source = v8::String::new(scope, source).unwrap(); + let value = v8::Script::compile(scope, source, None) + .unwrap() + .run(scope) + .unwrap(); + assert!( + moli_webidl::parse_dictionary::(scope, value, Context::argument("Test", 1)) + .is_err() + ); + } +}