Skip to content

Commit d3b4a96

Browse files
Drop serde_json arbitrary_precision from workspace (#5001)
Closes #4989 We were not using `arbitrary_precision` in the workspace: I only found the workspace dependency declaration itself plus a normal `serde_json::Number::from(3u8)` call in `crates/cli/src/spacetime_config.rs`, which does not require the feature. ## Summary - remove `serde_json/arbitrary_precision` from the workspace dependency declaration - keep `raw_value`, which is the only serde_json feature the workspace is clearly using here - avoid forcing a non-additive serde_json feature onto downstream consumers via feature unification ## Validation - `cargo tree -e features -p serde_json` - `cargo check -p spacetimedb-sdk -p spacetimedb-lib -p spacetimedb-sats -p spacetimedb-schema` --------- Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@users.noreply.github.com> Co-authored-by: Zeke Foppa <196249+bfops@users.noreply.github.com> Co-authored-by: Zeke Foppa <bfops@users.noreply.github.com>
1 parent 93a68ad commit d3b4a96

5 files changed

Lines changed: 69 additions & 8 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ second-stack = "0.3"
278278
self-replace = "1.5"
279279
semver = "1"
280280
serde = { version = "1.0.136", features = ["derive"] }
281-
serde_json = { version = "1.0.128", features = ["raw_value", "arbitrary_precision"] }
281+
serde_json = { version = "1.0.128", features = ["raw_value"] }
282282
serde_path_to_error = "0.1.9"
283283
serde_with = { version = "3.3.0", features = ["base64", "hex"] }
284284
serial_test = "2.0.0"

crates/bindings/tests/deps.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,39 @@ fn deptree_snapshot() -> std::io::Result<()> {
2525
Ok(())
2626
}
2727

28+
#[test]
29+
fn serde_json_arbitrary_precision_feature_boundaries() {
30+
// https://github.com/clockworklabs/SpacetimeDB/issues/4989
31+
// `serde_json/arbitrary_precision` is fine for internal tooling like the CLI,
32+
// but it should not be forced onto users compiling the Rust SDK or module
33+
// bindings. Cargo features are additive, so guard those public dependency
34+
// graphs explicitly.
35+
36+
// The CLI opts into it because `spacetime subscribe` reformats JSON rows
37+
// through `serde_json::Value`; without arbitrary precision, large SATS
38+
// integers like `ConnectionId` can be rounded before typed deserialization.
39+
assert_serde_json_arbitrary_precision("cargo tree -p spacetimedb-cli -e features,no-dev -i serde_json", true);
40+
assert_serde_json_arbitrary_precision(
41+
"cargo tree -p spacetimedb -e features,no-dev --target wasm32-unknown-unknown -i serde_json",
42+
false,
43+
);
44+
assert_serde_json_arbitrary_precision("cargo tree -p spacetimedb-sdk -e features,no-dev -i serde_json", false);
45+
assert_serde_json_arbitrary_precision(
46+
"cargo tree -p spacetimedb-sdk -e features,no-dev --features browser --target wasm32-unknown-unknown -i serde_json",
47+
false,
48+
);
49+
}
50+
51+
#[track_caller]
52+
fn assert_serde_json_arbitrary_precision(cmd: &str, expected: bool) {
53+
let tree = run_cmd(cmd);
54+
assert_eq!(
55+
tree.contains("serde_json feature \"arbitrary_precision\""),
56+
expected,
57+
"`arbitrary_precision` expectation failed for `{cmd}`:\n{tree}"
58+
);
59+
}
60+
2861
// runs a command string, split on spaces
2962
#[track_caller]
3063
fn run_cmd(cmd: &str) -> String {

crates/cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ regex.workspace = true
5858
reqwest.workspace = true
5959
rustyline.workspace = true
6060
serde = { workspace = true, features = ["derive"] }
61-
serde_json = { workspace = true, features = ["raw_value", "preserve_order"] }
61+
serde_json = { workspace = true, features = ["raw_value", "preserve_order", "arbitrary_precision"] }
6262
serde_with = { workspace = true, features = ["chrono_0_4"] }
6363
slab.workspace = true
6464
syntect.workspace = true

crates/cli/src/subcommands/subscribe.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,3 +767,32 @@ fn reformat_bsatn_rows(
767767
})
768768
.collect()
769769
}
770+
771+
#[cfg(test)]
772+
mod tests {
773+
use super::*;
774+
use spacetimedb_lib::sats::{
775+
algebraic_value::de::ValueDeserializer, de::Deserialize, GroundSpacetimeType, Typespace, WithTypespace,
776+
};
777+
use spacetimedb_lib::ConnectionId;
778+
779+
#[test]
780+
fn serde_json_value_preserves_connection_id_u128() -> anyhow::Result<()> {
781+
// `spacetime subscribe` reformats JSON rows through `serde_json::Value`
782+
// before typed SATS deserialization. The CLI enables
783+
// `serde_json/arbitrary_precision` so large `ConnectionId` values do not
784+
// get rounded while inside `Value`.
785+
let conn_id = ConnectionId::from_u128(u64::MAX as u128 + 1);
786+
let json = serde_json::to_string(&SerializeWrapper::new(&conn_id))?;
787+
let row = serde_json::from_str::<Value>(&json)?;
788+
789+
let typespace = Typespace::default();
790+
let conn_id_ty = ConnectionId::get_type();
791+
let conn_id_ty = WithTypespace::new(&typespace, &conn_id_ty);
792+
let de = serde::de::DeserializeSeed::deserialize(SeedWrapper(conn_id_ty), row)?;
793+
let de = ConnectionId::deserialize(ValueDeserializer::new(de)).unwrap();
794+
795+
assert_eq!(conn_id, de);
796+
Ok(())
797+
}
798+
}

crates/lib/src/connection_id.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -300,10 +300,9 @@ mod tests {
300300
use crate::WithTypespace;
301301

302302
proptest! {
303-
/// Tests the round-trip used when using the `spacetime subscribe`
304-
/// CLI command.
305-
/// Somewhat confusingly, this is distinct from the ser-de path
306-
/// in `test_serde_roundtrip`.
303+
/// Tests deserialization through the serde seed adapter without
304+
/// relying on `serde_json::Value` to preserve arbitrary-precision
305+
/// numbers.
307306
#[test]
308307
fn test_wrapper_roundtrip(val: u128) {
309308
let conn_id = ConnectionId::from_u128(val);
@@ -313,12 +312,12 @@ mod tests {
313312
let empty = Typespace::default();
314313
let conn_id_ty = ConnectionId::get_type();
315314
let conn_id_ty = WithTypespace::new(&empty, &conn_id_ty);
316-
let row = serde_json::from_str::<serde_json::Value>(&ser[..])?;
315+
let mut de = serde_json::Deserializer::from_str(&ser);
317316
let de = ::serde::de::DeserializeSeed::deserialize(
318317
crate::de::serde::SeedWrapper(
319318
conn_id_ty
320319
),
321-
row)?;
320+
&mut de)?;
322321
let de = ConnectionId::deserialize(ValueDeserializer::new(de)).unwrap();
323322
prop_assert_eq!(conn_id, de);
324323
}

0 commit comments

Comments
 (0)