diff --git a/.github/workflows/node.yaml b/.github/workflows/node.yaml index d2c6ae3f..67109b6b 100644 --- a/.github/workflows/node.yaml +++ b/.github/workflows/node.yaml @@ -174,7 +174,7 @@ jobs: run: yarn install --immutable --mode=skip-build - name: Build in docker - uses: addnab/docker-run-action@v3 + uses: stefan-gorules/docker-run-action@v1 if: ${{ matrix.settings.docker }} with: image: ${{ matrix.settings.docker }} @@ -315,7 +315,7 @@ jobs: shell: bash - name: Test bindings - uses: addnab/docker-run-action@v3 + uses: stefan-gorules/docker-run-action@v1 with: image: node:${{ matrix.node }}-slim options: -v ${{ github.workspace }}:/workspace @@ -370,7 +370,7 @@ jobs: shell: bash - name: Test bindings - uses: addnab/docker-run-action@v3 + uses: stefan-gorules/docker-run-action@v1 with: image: node:${{ matrix.node }}-alpine options: -v ${{ github.workspace }}:/workspace @@ -432,7 +432,7 @@ jobs: shell: bash - name: Test bindings - uses: addnab/docker-run-action@v3 + uses: stefan-gorules/docker-run-action@v1 with: options: --platform linux/arm64 -v ${{ github.workspace }}:/workspace image: node:${{ matrix.node }}-alpine @@ -482,7 +482,7 @@ jobs: # shell: bash # # - name: Setup and run tests - # uses: addnab/docker-run-action@v3 + # uses: stefan-gorules/docker-run-action@v1 # with: # image: ghcr.io/napi-rs/napi-rs/nodejs:aarch64-${{ matrix.node }} # options: -v ${{ github.workspace }}:/workspace -w /workspace diff --git a/.github/workflows/rust.yaml b/.github/workflows/rust.yaml index dd7b6ee4..5b4907d7 100644 --- a/.github/workflows/rust.yaml +++ b/.github/workflows/rust.yaml @@ -54,6 +54,10 @@ jobs: run: rustup install 1.80 - run: cargo test --workspace --all-features --exclude zen-ffi --exclude zen-nodejs --exclude zen-python - run: cargo test --workspace --all-features --exclude zen-ffi --exclude zen-nodejs --exclude zen-python --release + - name: Test without arbitrary_precision + run: cargo test --workspace --exclude zen-ffi --exclude zen-nodejs --exclude zen-python + - name: Test without arbitrary_precision (release) + run: cargo test --workspace --exclude zen-ffi --exclude zen-nodejs --exclude zen-python --release build: name: cargo +${{ matrix.rust }} build diff --git a/actions/cargo-version-action/dist/index.js b/actions/cargo-version-action/dist/index.js index 06c946e0..f409ab22 100644 --- a/actions/cargo-version-action/dist/index.js +++ b/actions/cargo-version-action/dist/index.js @@ -34066,17 +34066,12 @@ var toml = __nccwpck_require__(2132); ;// CONCATENATED MODULE: ./src/cargo.ts const versionRegex = /version = "[0-9]+\.[0-9]+\.[0-9]+"$/im; -const expressionDep = /zen-expression =.*$/im; -const templateDep = /zen-tmpl =.*$/im; -const macroDep = /zen-macros =.*$/im; -const typesDep = /zen-types =.*$/im; +const zenDepLine = /^.*zen-(?:expression|tmpl|macros|types)\s*=\s*\{.*}.*$/gim; +const depVersionRegex = /version\s*=\s*"[0-9]+\.[0-9]+\.[0-9]+"/; const updateCargoContents = (contents, { version }) => { return contents .replace(versionRegex, `version = "${version}"`) - .replace(expressionDep, `zen-expression = { path = "../expression", version = "${version}" }`) - .replace(macroDep, `zen-macros = { path = "../macros", version = "${version}" }`) - .replace(typesDep, `zen-types = { path = "../types", version = "${version}" }`) - .replace(templateDep, `zen-tmpl = { path = "../template", version = "${version}" }`); + .replace(zenDepLine, (line) => line.replace(depVersionRegex, `version = "${version}"`)); }; const getCargoVersion = (contents) => { var _a; diff --git a/actions/cargo-version-action/src/cargo.ts b/actions/cargo-version-action/src/cargo.ts index 2ce55e92..278e9de0 100644 --- a/actions/cargo-version-action/src/cargo.ts +++ b/actions/cargo-version-action/src/cargo.ts @@ -5,18 +5,13 @@ type UpdateCargoOptions = { }; const versionRegex = /version = "[0-9]+\.[0-9]+\.[0-9]+"$/im; -const expressionDep = /zen-expression =.*$/im; -const templateDep = /zen-tmpl =.*$/im; -const macroDep = /zen-macros =.*$/im; -const typesDep = /zen-types =.*$/im; +const zenDepLine = /^.*zen-(?:expression|tmpl|macros|types)\s*=\s*\{.*}.*$/gim; +const depVersionRegex = /version\s*=\s*"[0-9]+\.[0-9]+\.[0-9]+"/; export const updateCargoContents = (contents: string, { version }: UpdateCargoOptions): string => { return contents .replace(versionRegex, `version = "${version}"`) - .replace(expressionDep, `zen-expression = { path = "../expression", version = "${version}" }`) - .replace(macroDep, `zen-macros = { path = "../macros", version = "${version}" }`) - .replace(typesDep, `zen-types = { path = "../types", version = "${version}" }`) - .replace(templateDep, `zen-tmpl = { path = "../template", version = "${version}" }`); + .replace(zenDepLine, (line) => line.replace(depVersionRegex, `version = "${version}"`)); }; export const getCargoVersion = (contents: string): string => { diff --git a/actions/cargo-version-action/src/index.spec.ts b/actions/cargo-version-action/src/index.spec.ts index 9603ba31..3fa4d478 100644 --- a/actions/cargo-version-action/src/index.spec.ts +++ b/actions/cargo-version-action/src/index.spec.ts @@ -53,6 +53,34 @@ describe('GitHub Action', () => { } }); + test('Preserves default-features and features flags', () => { + const tomlWithFlags = ` + [package] + name = "zen-engine" + version = "0.53.0" + + [dependencies] + zen-types = { path = "../types", version = "0.53.0", default-features = false } + zen-expression = { path = "../expression", version = "0.53.0", default-features = false } + zen-tmpl = { path = "../template", version = "0.53.0", default-features = false } + zen-macros = { path = "../macros", version = "0.53.0" } +`; + + const expected = ` + [package] + name = "zen-engine" + version = "1.0.0" + + [dependencies] + zen-types = { path = "../types", version = "1.0.0", default-features = false } + zen-expression = { path = "../expression", version = "1.0.0", default-features = false } + zen-tmpl = { path = "../template", version = "1.0.0", default-features = false } + zen-macros = { path = "../macros", version = "1.0.0" } +`; + + expect(updateCargoContents(tomlWithFlags, { version: '1.0.0' })).toEqual(expected); + }); + test('Points to right directory', async () => { const escapeDir = (count: number) => '../'.repeat(count); const coreDirectory = path.join(__dirname, escapeDir(3), 'core'); diff --git a/bindings/c/Cargo.toml b/bindings/c/Cargo.toml index 7641efc6..ffb31ade 100644 --- a/bindings/c/Cargo.toml +++ b/bindings/c/Cargo.toml @@ -12,8 +12,8 @@ serde = { workspace = true } serde_json = { workspace = true } strum = { workspace = true, features = ["derive"] } tokio = { workspace = true, features = ["rt"] } -zen-engine = { path = "../../core/engine" } -zen-expression = { path = "../../core/expression" } +zen-engine = { path = "../../core/engine", features = ["arbitrary_precision"] } +zen-expression = { path = "../../core/expression", features = ["arbitrary_precision"] } zen-tmpl = { path = "../../core/template" } [lib] diff --git a/bindings/nodejs/Cargo.toml b/bindings/nodejs/Cargo.toml index f7488dd9..5bc273ee 100644 --- a/bindings/nodejs/Cargo.toml +++ b/bindings/nodejs/Cargo.toml @@ -15,8 +15,8 @@ napi-sys = "3" napi-derive = "3" tokio-util = { workspace = true, features = ["rt"] } serde_json = { workspace = true } -zen-engine = { path = "../../core/engine" } -zen-expression = { path = "../../core/expression" } +zen-engine = { path = "../../core/engine", features = ["arbitrary_precision"] } +zen-expression = { path = "../../core/expression", features = ["arbitrary_precision"] } zen-tmpl = { path = "../../core/template" } serde = { workspace = true, features = ["derive"] } json_dotpath = { workspace = true } diff --git a/bindings/python/Cargo.toml b/bindings/python/Cargo.toml index bd323e73..052a88b0 100644 --- a/bindings/python/Cargo.toml +++ b/bindings/python/Cargo.toml @@ -20,6 +20,6 @@ serde = { workspace = true } serde_json = { workspace = true } rust_decimal = { workspace = true, features = ["maths-nopanic"] } tokio-util = { version = "0.7", features = ["rt"] } -zen-engine = { path = "../../core/engine" } -zen-expression = { path = "../../core/expression" } +zen-engine = { path = "../../core/engine", features = ["arbitrary_precision"] } +zen-expression = { path = "../../core/expression", features = ["arbitrary_precision"] } zen-tmpl = { path = "../../core/template" } diff --git a/bindings/uniffi/Cargo.toml b/bindings/uniffi/Cargo.toml index 8315f77b..570b904c 100644 --- a/bindings/uniffi/Cargo.toml +++ b/bindings/uniffi/Cargo.toml @@ -15,8 +15,8 @@ path = "uniffi-bindgen.rs" [dependencies] uniffi = { version = "0.29", features = ["tokio", "cli"] } serde_json = { workspace = true } -zen-engine = { path = "../../core/engine" } -zen-expression = { path = "../../core/expression" } +zen-engine = { path = "../../core/engine", features = ["arbitrary_precision"] } +zen-expression = { path = "../../core/expression", features = ["arbitrary_precision"] } serde = { workspace = true, features = ["derive"] } async-trait = "0.1" tokio = "1.46" diff --git a/core/engine/Cargo.toml b/core/engine/Cargo.toml index 1500bb04..22e5c4ba 100644 --- a/core/engine/Cargo.toml +++ b/core/engine/Cargo.toml @@ -15,7 +15,7 @@ ahash = { workspace = true } anyhow = { workspace = true } thiserror = { workspace = true } petgraph = { workspace = true } -serde_json = { workspace = true, features = ["arbitrary_precision"] } +serde_json = { workspace = true, features = [] } serde = { workspace = true, features = ["derive", "rc"] } strum = { workspace = true, features = ["derive"] } once_cell = { workspace = true } @@ -52,4 +52,6 @@ harness = false name = "engine" [features] -bindgen = ["rquickjs/bindgen"] \ No newline at end of file +default = [] +bindgen = ["rquickjs/bindgen"] +arbitrary_precision = ["serde_json/arbitrary_precision", "zen-expression/arbitrary_precision", "zen-types/arbitrary_precision", "zen-tmpl/arbitrary_precision"] \ No newline at end of file diff --git a/core/expression/Cargo.toml b/core/expression/Cargo.toml index da015734..17cc30a0 100644 --- a/core/expression/Cargo.toml +++ b/core/expression/Cargo.toml @@ -19,7 +19,7 @@ once_cell = { workspace = true } regex = { workspace = true, optional = true } regex-lite = { workspace = true, optional = true } serde = { workspace = true, features = ["rc", "derive"] } -serde_json = { workspace = true, features = ["arbitrary_precision"] } +serde_json = { workspace = true, features = [] } strum = { workspace = true } strum_macros = { workspace = true } thiserror = { workspace = true } @@ -44,6 +44,7 @@ recursive = { workspace = true } default = ["regex-deprecated"] regex-lite = ["dep:regex-lite"] regex-deprecated = ["dep:regex"] +arbitrary_precision = ["serde_json/arbitrary_precision", "zen-types/arbitrary_precision"] [[bench]] harness = false diff --git a/core/template/Cargo.toml b/core/template/Cargo.toml index eef066b1..bffc9830 100644 --- a/core/template/Cargo.toml +++ b/core/template/Cargo.toml @@ -17,4 +17,5 @@ serde_json = { workspace = true } default = ["regex-deprecated"] regex-lite = ["zen-expression/regex-lite"] -regex-deprecated = ["zen-expression/regex-deprecated"] \ No newline at end of file +regex-deprecated = ["zen-expression/regex-deprecated"] +arbitrary_precision = ["zen-expression/arbitrary_precision"] \ No newline at end of file diff --git a/core/types/Cargo.toml b/core/types/Cargo.toml index 4318c4f5..2da2fa56 100644 --- a/core/types/Cargo.toml +++ b/core/types/Cargo.toml @@ -8,8 +8,12 @@ license = "MIT" [dependencies] ahash = { workspace = true } serde = { workspace = true, features = ["rc", "derive"] } -serde_json = { workspace = true, features = ["arbitrary_precision"] } +serde_json = { workspace = true, features = [] } rust_decimal = { workspace = true, features = ["maths-nopanic"] } rust_decimal_macros = { workspace = true } thiserror = { workspace = true } nohash-hasher = { workspace = true } + +[features] +default = [] +arbitrary_precision = ["serde_json/arbitrary_precision"] diff --git a/core/types/src/constant.rs b/core/types/src/constant.rs index d4ba663d..4e0eeedb 100644 --- a/core/types/src/constant.rs +++ b/core/types/src/constant.rs @@ -1 +1,2 @@ +#[cfg(feature = "arbitrary_precision")] pub(crate) const NUMBER_TOKEN: &str = "$serde_json::private::Number"; diff --git a/core/types/src/rcvalue/conv.rs b/core/types/src/rcvalue/conv.rs index 1cf1a758..cf2f51ac 100644 --- a/core/types/src/rcvalue/conv.rs +++ b/core/types/src/rcvalue/conv.rs @@ -1,6 +1,8 @@ use crate::rcvalue::RcValue; use crate::variable::{ToVariable, Variable}; use rust_decimal::Decimal; +#[cfg(not(feature = "arbitrary_precision"))] +use rust_decimal::prelude::FromPrimitive; use serde_json::Value; use std::rc::Rc; @@ -58,11 +60,35 @@ impl From<&Value> for RcValue { match value { Value::Null => RcValue::Null, Value::Bool(b) => RcValue::Bool(*b), - Value::Number(n) => RcValue::Number( - Decimal::from_str_exact(n.as_str()) - .or_else(|_| Decimal::from_scientific(n.as_str())) - .expect("Allowed number"), - ), + Value::Number(n) => { + #[cfg(feature = "arbitrary_precision")] + { + RcValue::Number( + Decimal::from_str_exact(n.as_str()) + .or_else(|_| Decimal::from_scientific(n.as_str())) + .expect("Allowed number"), + ) + } + + #[cfg(not(feature = "arbitrary_precision"))] + { + if let Some(u) = n.as_u64() { + return RcValue::Number(Decimal::from(u)); + } + + if let Some(i) = n.as_i64() { + return RcValue::Number(Decimal::from(i)); + } + + if let Some(f) = n.as_f64() { + return RcValue::Number(Decimal::from_f64(f).expect("Allowed number")); + } + + unreachable!( + "serde_json::Number is always u64, i64, or f64 without arbitrary_precision" + ); + } + } Value::String(s) => RcValue::String(Rc::from(s.as_str())), Value::Array(arr) => RcValue::Array(arr.iter().map(RcValue::from).collect()), Value::Object(obj) => RcValue::Object( diff --git a/core/types/src/rcvalue/de.rs b/core/types/src/rcvalue/de.rs index 087ef1b7..2d52fcef 100644 --- a/core/types/src/rcvalue/de.rs +++ b/core/types/src/rcvalue/de.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "arbitrary_precision")] use crate::constant::NUMBER_TOKEN; use crate::rcvalue::RcValue; use ahash::{HashMap, HashMapExt}; @@ -7,6 +8,7 @@ use serde::de::{DeserializeSeed, Error, MapAccess, SeqAccess, Unexpected, Visito use serde::{Deserialize, Deserializer}; use std::fmt::Formatter; use std::marker::PhantomData; +#[cfg(feature = "arbitrary_precision")] use std::ops::Deref; use std::rc::Rc; @@ -84,11 +86,13 @@ impl<'de> Visitor<'de> for RcValueVisitor { A: MapAccess<'de>, { let mut m = HashMap::with_capacity(map.size_hint().unwrap_or_default()); + #[cfg(feature = "arbitrary_precision")] let mut first = true; while let Some((key, value)) = map.next_entry_seed(PhantomData::>, RcValueDeserializer)? { + #[cfg(feature = "arbitrary_precision")] if first && key.deref() == NUMBER_TOKEN { let str = match &value { RcValue::String(s) => s.as_ref(), @@ -103,7 +107,10 @@ impl<'de> Visitor<'de> for RcValueVisitor { } m.insert(key, value); - first = false; + #[cfg(feature = "arbitrary_precision")] + { + first = false; + } } Ok(RcValue::Object(m)) diff --git a/core/types/src/rcvalue/ser.rs b/core/types/src/rcvalue/ser.rs index 0cd6ca5a..9e18f072 100644 --- a/core/types/src/rcvalue/ser.rs +++ b/core/types/src/rcvalue/ser.rs @@ -1,8 +1,13 @@ -use crate::constant::NUMBER_TOKEN; use crate::rcvalue::RcValue; -use serde::ser::SerializeStruct; +#[cfg(not(feature = "arbitrary_precision"))] +use rust_decimal::prelude::ToPrimitive; use serde::{Serialize, Serializer}; +#[cfg(feature = "arbitrary_precision")] +use crate::constant::NUMBER_TOKEN; +#[cfg(feature = "arbitrary_precision")] +use serde::ser::SerializeStruct; + impl Serialize for RcValue { fn serialize(&self, serializer: S) -> Result where @@ -11,6 +16,7 @@ impl Serialize for RcValue { match self { RcValue::Null => serializer.serialize_unit(), RcValue::Bool(v) => serializer.serialize_bool(*v), + #[cfg(feature = "arbitrary_precision")] RcValue::Number(v) => { let str = v.normalize().to_string(); @@ -18,6 +24,22 @@ impl Serialize for RcValue { s.serialize_field(NUMBER_TOKEN, &str)?; s.end() } + #[cfg(not(feature = "arbitrary_precision"))] + RcValue::Number(v) => { + if let Some(u) = v.to_u64() { + return serializer.serialize_u64(u); + } + + if let Some(i) = v.to_i64() { + return serializer.serialize_i64(i); + } + + if let Some(f) = v.to_f64() { + return serializer.serialize_f64(f); + } + + Err(serde::ser::Error::custom("cannot convert to f64")) + } RcValue::String(v) => serializer.serialize_str(v), RcValue::Array(v) => serializer.collect_seq(v.iter()), RcValue::Object(v) => serializer.collect_map(v.iter()), diff --git a/core/types/src/variable/conv.rs b/core/types/src/variable/conv.rs index b81137d1..284ce4ce 100644 --- a/core/types/src/variable/conv.rs +++ b/core/types/src/variable/conv.rs @@ -1,18 +1,46 @@ use crate::variable::Variable; use rust_decimal::Decimal; +#[cfg(not(feature = "arbitrary_precision"))] +use rust_decimal::prelude::FromPrimitive; use serde_json::{Number, Value}; use std::rc::Rc; +#[cfg(not(feature = "arbitrary_precision"))] +use std::str::FromStr; impl From for Variable { fn from(value: Value) -> Self { match value { Value::Null => Variable::Null, Value::Bool(b) => Variable::Bool(b), - Value::Number(n) => Variable::Number( - Decimal::from_str_exact(n.as_str()) - .or_else(|_| Decimal::from_scientific(n.as_str())) - .expect("Allowed number"), - ), + Value::Number(n) => { + #[cfg(feature = "arbitrary_precision")] + { + Variable::Number( + Decimal::from_str_exact(n.as_str()) + .or_else(|_| Decimal::from_scientific(n.as_str())) + .expect("Allowed number"), + ) + } + + #[cfg(not(feature = "arbitrary_precision"))] + { + if let Some(n) = n.as_u64() { + return Variable::Number(n.into()); + } + + if let Some(n) = n.as_i64() { + return Variable::Number(n.into()); + } + + if let Some(n) = n.as_f64() { + return Variable::Number(Decimal::from_f64(n).expect("Allowed number")); + } + + unreachable!( + "serde_json::Number is always u64, i64, or f64 without arbitrary_precision" + ) + } + } Value::String(s) => Variable::String(Rc::from(s.as_str())), Value::Array(arr) => { Variable::from_array(arr.into_iter().map(Variable::from).collect()) @@ -31,11 +59,35 @@ impl From<&Value> for Variable { match value { Value::Null => Variable::Null, Value::Bool(b) => Variable::Bool(*b), - Value::Number(n) => Variable::Number( - Decimal::from_str_exact(n.as_str()) - .or_else(|_| Decimal::from_scientific(n.as_str())) - .expect("Allowed number"), - ), + Value::Number(n) => { + #[cfg(feature = "arbitrary_precision")] + { + Variable::Number( + Decimal::from_str_exact(n.as_str()) + .or_else(|_| Decimal::from_scientific(n.as_str())) + .expect("Allowed number"), + ) + } + + #[cfg(not(feature = "arbitrary_precision"))] + { + if let Some(u) = n.as_u64() { + return Variable::Number(u.into()); + } + + if let Some(i) = n.as_i64() { + return Variable::Number(i.into()); + } + + if let Some(f) = n.as_f64() { + return Variable::Number(Decimal::from_f64(f).expect("Allowed number")); + } + + unreachable!( + "serde_json::Number is always u64, i64, or f64 without arbitrary_precision" + ); + } + } Value::String(s) => Variable::String(Rc::from(s.as_str())), Value::Array(arr) => Variable::from_array(arr.iter().map(Variable::from).collect()), Value::Object(obj) => Variable::from_object( @@ -53,7 +105,17 @@ impl From for Value { Variable::Null => Value::Null, Variable::Bool(b) => Value::Bool(b), Variable::Number(n) => { - Value::Number(Number::from_string_unchecked(n.normalize().to_string())) + #[cfg(feature = "arbitrary_precision")] + { + Value::Number(Number::from_string_unchecked(n.normalize().to_string())) + } + #[cfg(not(feature = "arbitrary_precision"))] + { + Value::Number( + Number::from_str(n.normalize().to_string().as_str()) + .expect("Allowed number"), + ) + } } Variable::String(s) => Value::String(s.to_string()), Variable::Array(arr) => { diff --git a/core/types/src/variable/de.rs b/core/types/src/variable/de.rs index 6c3ad6fd..75b78c6d 100644 --- a/core/types/src/variable/de.rs +++ b/core/types/src/variable/de.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "arbitrary_precision")] use crate::constant::NUMBER_TOKEN; use crate::variable::Variable; use ahash::{HashMap, HashMapExt}; @@ -7,6 +8,7 @@ use serde::de::{DeserializeSeed, Error, MapAccess, SeqAccess, Unexpected, Visito use serde::{Deserialize, Deserializer}; use std::fmt::Formatter; use std::marker::PhantomData; +#[cfg(feature = "arbitrary_precision")] use std::ops::Deref; use std::rc::Rc; @@ -84,11 +86,13 @@ impl<'de> Visitor<'de> for VariableVisitor { A: MapAccess<'de>, { let mut m = HashMap::with_capacity(map.size_hint().unwrap_or_default()); + #[cfg(feature = "arbitrary_precision")] let mut first = true; while let Some((key, value)) = map.next_entry_seed(PhantomData::>, VariableDeserializer)? { + #[cfg(feature = "arbitrary_precision")] if first && key.deref() == NUMBER_TOKEN { let str = value .as_str() @@ -102,7 +106,10 @@ impl<'de> Visitor<'de> for VariableVisitor { } m.insert(key, value); - first = false; + #[cfg(feature = "arbitrary_precision")] + { + first = false; + } } Ok(Variable::from_object(m)) diff --git a/core/types/src/variable/ser.rs b/core/types/src/variable/ser.rs index 1f57b709..304f199b 100644 --- a/core/types/src/variable/ser.rs +++ b/core/types/src/variable/ser.rs @@ -1,8 +1,13 @@ -use crate::constant::NUMBER_TOKEN; use crate::variable::Variable; -use serde::ser::SerializeStruct; +#[cfg(not(feature = "arbitrary_precision"))] +use rust_decimal::prelude::ToPrimitive; use serde::{Serialize, Serializer}; +#[cfg(feature = "arbitrary_precision")] +use crate::constant::NUMBER_TOKEN; +#[cfg(feature = "arbitrary_precision")] +use serde::ser::SerializeStruct; + impl Serialize for Variable { fn serialize(&self, serializer: S) -> Result where @@ -11,6 +16,7 @@ impl Serialize for Variable { match self { Variable::Null => serializer.serialize_unit(), Variable::Bool(v) => serializer.serialize_bool(*v), + #[cfg(feature = "arbitrary_precision")] Variable::Number(v) => { let str = v.normalize().to_string(); @@ -18,6 +24,22 @@ impl Serialize for Variable { s.serialize_field(NUMBER_TOKEN, &str)?; s.end() } + #[cfg(not(feature = "arbitrary_precision"))] + Variable::Number(v) => { + if let Some(u) = v.to_u64() { + return serializer.serialize_u64(u); + } + + if let Some(i) = v.to_i64() { + return serializer.serialize_i64(i); + } + + if let Some(f) = v.to_f64() { + return serializer.serialize_f64(f); + } + + Err(serde::ser::Error::custom("cannot convert to f64")) + } Variable::String(v) => serializer.serialize_str(v), Variable::Array(v) => { let borrowed = v.borrow();