Skip to content

Commit

Permalink
Allow deserialization of missing objects properties into Option<> (#3767
Browse files Browse the repository at this point in the history
)

* Allow deserialization of missing objects properties into Option<>

Currently the TryFromJs derive macro requires that all properties
be defined in the object, whether they are declared as Option<> or
not. This commit makes it so if the field is not found in the
JsObject, we try to deserialize undefined.

This is more in line with JavaScript behaviour.

* Fix cargo fmt

* Fix clippies

* Empty commit to trigger rerun of workflow

* Remove circular dependency between boa_engine and boa_macros

* Missing "::" in macro

---------

Co-authored-by: jedel1043 <jedel0124@gmail.com>
  • Loading branch information
hansl and jedel1043 authored Mar 28, 2024
1 parent fb8e16b commit 08b0deb
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 24 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ serde = "1.0.197"
static_assertions = "1.1.0"
textwrap = "0.16.0"
thin-vec = "0.2.13"
time = {version = "0.3.34", no-default-features = true, features = ["local-offset", "large-dates", "wasm-bindgen", "parsing", "formatting", "macros"]}
time = {version = "0.3.34", default-features = false, features = ["local-offset", "large-dates", "wasm-bindgen", "parsing", "formatting", "macros"]}
tinystr = "0.7.5"
log = "0.4.21"
simple_logger = "4.3.3"
Expand Down
31 changes: 10 additions & 21 deletions core/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,30 +418,19 @@ fn generate_conversion(fields: FieldsNamed) -> Result<proc_macro2::TokenStream,
.map_err(|err| vec![err])?;
}

final_fields.push(quote! {
let #name = match props.get(&::boa_engine::js_string!(#name_str).into()) {
Some(pd) => pd.value().ok_or_else(|| ::boa_engine::JsError::from(
::boa_engine::JsNativeError::typ().with_message(#error_str)
))?.clone().try_js_into(context)?,
None => ::boa_engine::JsValue::undefined().try_js_into(context)?,
};
});

if let Some(method) = from_js_with {
let ident = Ident::new(&method.value(), method.span());
final_fields.push(quote! {
let #name = #ident(props.get(&::boa_engine::js_string!(#name_str).into()).ok_or_else(|| {
::boa_engine::JsError::from(
boa_engine::JsNativeError::typ().with_message(#error_str)
)
})?.value().ok_or_else(|| {
::boa_engine::JsError::from(
boa_engine::JsNativeError::typ().with_message(#error_str)
)
})?, context)?;
});
} else {
final_fields.push(quote! {
let #name = props.get(&::boa_engine::js_string!(#name_str).into()).ok_or_else(|| {
::boa_engine::JsError::from(
boa_engine::JsNativeError::typ().with_message(#error_str)
)
})?.value().ok_or_else(|| {
::boa_engine::JsError::from(
boa_engine::JsNativeError::typ().with_message(#error_str)
)
})?.clone().try_js_into(context)?;
let #name = #ident(&#name, context)?;
});
}
}
Expand Down
1 change: 0 additions & 1 deletion tests/macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ rust-version.workspace = true

[dev-dependencies]
trybuild = "1.0.90"
boa_macros.workspace = true
boa_engine.workspace = true

[lints]
Expand Down
File renamed without changes.
67 changes: 67 additions & 0 deletions tests/macros/tests/optional.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#![allow(unused_crate_dependencies)]

use boa_engine::value::TryFromJs;
use boa_engine::Source;

#[derive(PartialEq, Eq, TryFromJs)]
struct Deserialize {
required: String,
optional: Option<String>,
}

#[test]
fn optional_missing_try_from_js() {
let mut context = boa_engine::Context::default();
let value = context
.eval(Source::from_bytes(
r#"
let empty = {
"required":"foo",
};
empty
"#,
))
.unwrap();

let deserialized: Deserialize = Deserialize::try_from_js(&value, &mut context).unwrap();
assert_eq!(deserialized.required, "foo");
assert_eq!(deserialized.optional, None);
}

#[test]
fn optional_try_from_js() {
let mut context = boa_engine::Context::default();
let value = context
.eval(Source::from_bytes(
r#"
let empty = {
"required": "foo",
"optional": "bar",
};
empty
"#,
))
.unwrap();

let deserialized: Deserialize = Deserialize::try_from_js(&value, &mut context).unwrap();
assert_eq!(deserialized.required, "foo");
assert_eq!(deserialized.optional, Some("bar".to_string()));
}

#[test]
fn required_missing_try_from_js() {
let mut context = boa_engine::Context::default();
let value = context
.eval(Source::from_bytes(
r"
let value = {};
value
",
))
.unwrap();

assert!(
Deserialize::try_from_js(&value, &mut context).is_err(),
"foo"
);
}

0 comments on commit 08b0deb

Please sign in to comment.