Skip to content

Commit 39afe4c

Browse files
committed
Add SerializeOptions::detect_serde_json_arbitrary_precision to detect serde_json::Number with arbitrary_precision and convert it to Lua number.
By default the option is disabled and such numbers represented as Lua objects with `$serde_json::private::Number` key. Fixes #385
1 parent 038cc5f commit 39afe4c

File tree

3 files changed

+109
-9
lines changed

3 files changed

+109
-9
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ http-body-util = "0.1.1"
6969
reqwest = { version = "0.12", features = ["json"] }
7070
tokio = { version = "1.0", features = ["macros", "rt", "time"] }
7171
serde = { version = "1.0", features = ["derive"] }
72-
serde_json = "1.0"
72+
serde_json = { version = "1.0", features = ["arbitrary_precision"] }
7373
maplit = "1.0"
7474
tempfile = "3"
7575
static_assertions = "1.0"

src/serde/ser.rs

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ pub struct Options {
4343
/// [`null`]: crate::LuaSerdeExt::null
4444
/// [`Nil`]: crate::Value::Nil
4545
pub serialize_unit_to_null: bool,
46+
47+
/// If true, serialize `serde_json::Number` with arbitrary_precision to a Lua number.
48+
/// Otherwise it will be serialized as an object (what serde does).
49+
///
50+
/// Default: **false**
51+
pub detect_serde_json_arbitrary_precision: bool,
4652
}
4753

4854
impl Default for Options {
@@ -58,6 +64,7 @@ impl Options {
5864
set_array_metatable: true,
5965
serialize_none_to_null: true,
6066
serialize_unit_to_null: true,
67+
detect_serde_json_arbitrary_precision: false,
6168
}
6269
}
6370

@@ -87,6 +94,20 @@ impl Options {
8794
self.serialize_unit_to_null = enabled;
8895
self
8996
}
97+
98+
/// Sets [`detect_serde_json_arbitrary_precision`] option.
99+
///
100+
/// This option is used to serialize `serde_json::Number` with arbitrary precision to a Lua number.
101+
/// Otherwise it will be serialized as an object (what serde does).
102+
///
103+
/// This option is disabled by default.
104+
///
105+
/// [`detect_serde_json_arbitrary_precision`]: #structfield.detect_serde_json_arbitrary_precision
106+
#[must_use]
107+
pub const fn detect_serde_json_arbitrary_precision(mut self, enabled: bool) -> Self {
108+
self.detect_serde_json_arbitrary_precision = enabled;
109+
self
110+
}
90111
}
91112

92113
impl<'lua> Serializer<'lua> {
@@ -121,7 +142,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
121142
type SerializeTupleStruct = SerializeSeq<'lua>;
122143
type SerializeTupleVariant = SerializeTupleVariant<'lua>;
123144
type SerializeMap = SerializeMap<'lua>;
124-
type SerializeStruct = SerializeMap<'lua>;
145+
type SerializeStruct = SerializeStruct<'lua>;
125146
type SerializeStructVariant = SerializeStructVariant<'lua>;
126147

127148
#[inline]
@@ -282,8 +303,23 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
282303
}
283304

284305
#[inline]
285-
fn serialize_struct(self, _name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
286-
self.serialize_map(Some(len))
306+
fn serialize_struct(self, name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
307+
if self.options.detect_serde_json_arbitrary_precision
308+
&& name == "$serde_json::private::Number"
309+
&& len == 1
310+
{
311+
return Ok(SerializeStruct {
312+
lua: self.lua,
313+
inner: None,
314+
options: self.options,
315+
});
316+
}
317+
318+
Ok(SerializeStruct {
319+
lua: self.lua,
320+
inner: Some(Value::Table(self.lua.create_table_with_capacity(0, len)?)),
321+
options: self.options,
322+
})
287323
}
288324

289325
#[inline]
@@ -465,20 +501,53 @@ impl<'lua> ser::SerializeMap for SerializeMap<'lua> {
465501
}
466502
}
467503

468-
impl<'lua> ser::SerializeStruct for SerializeMap<'lua> {
504+
#[doc(hidden)]
505+
pub struct SerializeStruct<'lua> {
506+
lua: &'lua Lua,
507+
inner: Option<Value<'lua>>,
508+
options: Options,
509+
}
510+
511+
impl<'lua> ser::SerializeStruct for SerializeStruct<'lua> {
469512
type Ok = Value<'lua>;
470513
type Error = Error;
471514

472515
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
473516
where
474517
T: Serialize + ?Sized,
475518
{
476-
ser::SerializeMap::serialize_key(self, key)?;
477-
ser::SerializeMap::serialize_value(self, value)
519+
match self.inner {
520+
Some(Value::Table(ref table)) => {
521+
table.raw_set(key, self.lua.to_value_with(value, self.options)?)?;
522+
}
523+
None if self.options.detect_serde_json_arbitrary_precision => {
524+
// A special case for `serde_json::Number` with arbitrary precision.
525+
assert_eq!(key, "$serde_json::private::Number");
526+
self.inner = Some(self.lua.to_value_with(value, self.options)?);
527+
}
528+
_ => unreachable!(),
529+
}
530+
Ok(())
478531
}
479532

480533
fn end(self) -> Result<Value<'lua>> {
481-
ser::SerializeMap::end(self)
534+
match self.inner {
535+
Some(table @ Value::Table(_)) => Ok(table),
536+
Some(value) if self.options.detect_serde_json_arbitrary_precision => {
537+
let number_s = value.as_str().expect("not an arbitrary precision number");
538+
if number_s.contains(&['.', 'e', 'E']) {
539+
if let Ok(number) = number_s.parse().map(Value::Number) {
540+
return Ok(number);
541+
}
542+
}
543+
Ok(number_s
544+
.parse()
545+
.map(Value::Integer)
546+
.or_else(|_| number_s.parse().map(Value::Number))
547+
.unwrap_or_else(|_| value))
548+
}
549+
_ => unreachable!(),
550+
}
482551
}
483552
}
484553

@@ -505,7 +574,7 @@ impl<'lua> ser::SerializeStructVariant for SerializeStructVariant<'lua> {
505574

506575
fn end(self) -> Result<Value<'lua>> {
507576
let lua = self.table.0.lua;
508-
let table = lua.create_table()?;
577+
let table = lua.create_table_with_capacity(0, 1)?;
509578
table.raw_set(self.name, self.table)?;
510579
Ok(Value::Table(table))
511580
}

tests/serde.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,3 +697,34 @@ fn test_from_value_sorted() -> Result<(), Box<dyn StdError>> {
697697

698698
Ok(())
699699
}
700+
701+
#[test]
702+
fn test_arbitrary_precision() {
703+
let lua = Lua::new();
704+
705+
let opts = SerializeOptions::new().detect_serde_json_arbitrary_precision(true);
706+
707+
// Number
708+
let num = serde_json::Value::Number(serde_json::Number::from_f64(1.244e2).unwrap());
709+
let num = lua.to_value_with(&num, opts).unwrap();
710+
assert_eq!(num, Value::Number(1.244e2));
711+
712+
// Integer
713+
let num = serde_json::Value::Number(serde_json::Number::from_f64(123.0).unwrap());
714+
let num = lua.to_value_with(&num, opts).unwrap();
715+
assert_eq!(num, Value::Integer(123));
716+
717+
// Max u64
718+
let num = serde_json::Value::Number(serde_json::Number::from(i64::MAX));
719+
let num = lua.to_value_with(&num, opts).unwrap();
720+
assert_eq!(num, Value::Number(i64::MAX as f64));
721+
722+
// Check that the option is disabled by default
723+
let num = serde_json::Value::Number(serde_json::Number::from_f64(1.244e2).unwrap());
724+
let num = lua.to_value(&num).unwrap();
725+
assert_eq!(num.type_name(), "table");
726+
assert_eq!(
727+
format!("{:#?}", num),
728+
"{\n [\"$serde_json::private::Number\"] = \"124.4\",\n}"
729+
);
730+
}

0 commit comments

Comments
 (0)