Skip to content

Commit ee2f1a8

Browse files
committed
Add strict mode
1 parent 58eac7b commit ee2f1a8

File tree

4 files changed

+46
-3
lines changed

4 files changed

+46
-3
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,16 @@ fn main() {
5151
}
5252
```
5353

54+
## Strict mode
55+
56+
The implementation tries to make smart guesses when it can, for example the deserializer will fallback to converting `BigInt` to `String`. This is likely not what you want if you implement a serialization that is JSON compliant.
57+
58+
This is s why we offer the `strict` mode, that will stick to what the behaviour that the Javascript specification defines for `JSON`. Just switch the method to use it.
59+
60+
```rust
61+
let u: User = rquickjs_serde::from_value_strict(val).unwrap();
62+
```
63+
5464
## Acknowledgements
5565

5666
This project includes code derived from the [Javy](https://github.com/bytecodealliance/javy) project. See [`NOTICE`](./NOTICE) for more details.

src/de.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,33 @@ enum ClassId {
4848
/// });
4949
/// ```
5050
pub struct Deserializer<'js> {
51-
pub value: Value<'js>,
51+
value: Value<'js>,
52+
/// In strict mode, only JSON-able values are allowed.
53+
strict: bool,
5254
map_key: bool,
5355
current_kv: Option<(Value<'js>, Value<'js>)>,
5456
/// Stack to track circular dependencies.
5557
stack: Vec<Value<'js>>,
5658
}
5759

60+
impl<'js> Deserializer<'js> {
61+
pub fn new(value: Value<'js>) -> Self {
62+
Self::from(value)
63+
}
64+
65+
pub fn with_strict(self) -> Self {
66+
Self {
67+
strict: true,
68+
..self
69+
}
70+
}
71+
}
72+
5873
impl<'de> From<Value<'de>> for Deserializer<'de> {
5974
fn from(value: Value<'de>) -> Self {
6075
Self {
6176
value,
77+
strict: false,
6278
map_key: false,
6379
current_kv: None,
6480
// We are probaby over allocating here. But it's probably fine to
@@ -220,7 +236,9 @@ impl<'de> de::Deserializer<'de> for &mut Deserializer<'de> {
220236
return self.deserialize_any(visitor);
221237
}
222238

223-
if let Some(f) = get_to_string(&self.value) {
239+
if let Some(f) = get_to_string(&self.value)
240+
&& !self.strict
241+
{
224242
let v: Value = f.call((This(self.value.clone()),)).map_err(Error::new)?;
225243
self.value = v;
226244
return self.deserialize_any(visitor);

src/err.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ impl fmt::Display for ErrorImpl {
8282
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
8383
match self {
8484
ErrorImpl::Message(msg) => write!(f, "{msg}"),
85-
ErrorImpl::Rquickjs(e) => write!(f, "{e}"),
85+
// JSError prefix is used by Javy because the serde_transcode.
86+
// Keep it as-is for compatibility.
87+
ErrorImpl::Rquickjs(e) => write!(f, "JSError: {e}"),
8688
}
8789
}
8890
}

src/lib.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,19 @@ where
127127
T::deserialize(&mut deserializer)
128128
}
129129

130+
/// Interpret a `rquickjs::Value` as an instance of type `T` in strict mode.
131+
///
132+
/// Strict mode will stick to the behaviour of JSON.stringify.
133+
///
134+
/// See [from_value] for more information about the deserialization.
135+
pub fn from_value_strict<T>(value: Value) -> Result<T>
136+
where
137+
T: DeserializeOwned,
138+
{
139+
let mut deserializer = Deserializer::from(value).with_strict();
140+
T::deserialize(&mut deserializer)
141+
}
142+
130143
#[cfg(test)]
131144
mod tests {
132145
use std::collections::BTreeMap;

0 commit comments

Comments
 (0)