commit cec3e4f9254644bbcf506e4a6cbf1fb2f55a991d
parent 855d61c9db35b76ed11591c51051dff88d6f6682
Author: triesap <tyson@radroots.org>
Date: Sat, 27 Jun 2026 09:44:12 +0000
events: preserve serde wire invariants
- restore feature-invariant listing farm defaults
- keep listing parse errors externally tagged on the runtime wire
- keep revision outcomes internally tagged without content wrappers
- pin dto_bindgen to the upstream serde-shape support commit
Diffstat:
4 files changed, 105 insertions(+), 13 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -1502,7 +1502,7 @@ checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
[[package]]
name = "dto_bindgen"
version = "0.1.0"
-source = "git+https://github.com/triesap/dto_bindgen?rev=a9e6433b88e95f4424dadd6b5cd7a610aa150dec#a9e6433b88e95f4424dadd6b5cd7a610aa150dec"
+source = "git+https://github.com/triesap/dto_bindgen?rev=0876852b478ef8acc94143d632030096de600ae9#0876852b478ef8acc94143d632030096de600ae9"
dependencies = [
"dto_bindgen_backend_python",
"dto_bindgen_backend_ts",
@@ -1514,7 +1514,7 @@ dependencies = [
[[package]]
name = "dto_bindgen_backend_python"
version = "0.1.0"
-source = "git+https://github.com/triesap/dto_bindgen?rev=a9e6433b88e95f4424dadd6b5cd7a610aa150dec#a9e6433b88e95f4424dadd6b5cd7a610aa150dec"
+source = "git+https://github.com/triesap/dto_bindgen?rev=0876852b478ef8acc94143d632030096de600ae9#0876852b478ef8acc94143d632030096de600ae9"
dependencies = [
"dto_bindgen_core",
]
@@ -1522,7 +1522,7 @@ dependencies = [
[[package]]
name = "dto_bindgen_backend_ts"
version = "0.1.0"
-source = "git+https://github.com/triesap/dto_bindgen?rev=a9e6433b88e95f4424dadd6b5cd7a610aa150dec#a9e6433b88e95f4424dadd6b5cd7a610aa150dec"
+source = "git+https://github.com/triesap/dto_bindgen?rev=0876852b478ef8acc94143d632030096de600ae9#0876852b478ef8acc94143d632030096de600ae9"
dependencies = [
"dto_bindgen_core",
]
@@ -1530,7 +1530,7 @@ dependencies = [
[[package]]
name = "dto_bindgen_core"
version = "0.1.0"
-source = "git+https://github.com/triesap/dto_bindgen?rev=a9e6433b88e95f4424dadd6b5cd7a610aa150dec#a9e6433b88e95f4424dadd6b5cd7a610aa150dec"
+source = "git+https://github.com/triesap/dto_bindgen?rev=0876852b478ef8acc94143d632030096de600ae9#0876852b478ef8acc94143d632030096de600ae9"
dependencies = [
"proc-macro2",
"quote",
@@ -1544,7 +1544,7 @@ dependencies = [
[[package]]
name = "dto_bindgen_macros"
version = "0.1.0"
-source = "git+https://github.com/triesap/dto_bindgen?rev=a9e6433b88e95f4424dadd6b5cd7a610aa150dec#a9e6433b88e95f4424dadd6b5cd7a610aa150dec"
+source = "git+https://github.com/triesap/dto_bindgen?rev=0876852b478ef8acc94143d632030096de600ae9#0876852b478ef8acc94143d632030096de600ae9"
dependencies = [
"proc-macro2",
"quote",
diff --git a/Cargo.toml b/Cargo.toml
@@ -58,8 +58,8 @@ homepage = "https://radroots.org"
readme = "README"
[workspace.dependencies]
-dto_bindgen = { git = "https://github.com/triesap/dto_bindgen", rev = "a9e6433b88e95f4424dadd6b5cd7a610aa150dec", package = "dto_bindgen" }
-dto_bindgen_core = { git = "https://github.com/triesap/dto_bindgen", rev = "a9e6433b88e95f4424dadd6b5cd7a610aa150dec", package = "dto_bindgen_core" }
+dto_bindgen = { git = "https://github.com/triesap/dto_bindgen", rev = "0876852b478ef8acc94143d632030096de600ae9", package = "dto_bindgen" }
+dto_bindgen_core = { git = "https://github.com/triesap/dto_bindgen", rev = "0876852b478ef8acc94143d632030096de600ae9", package = "dto_bindgen_core" }
radroots_core = { path = "crates/core", version = "0.1.0-alpha.2", default-features = false }
radroots_events = { path = "crates/events", version = "0.1.0-alpha.2", default-features = false }
radroots_event_store = { path = "crates/event_store", version = "0.1.0-alpha.2", default-features = false }
diff --git a/crates/events/src/listing.rs b/crates/events/src/listing.rs
@@ -72,7 +72,7 @@ pub struct RadrootsListing {
serde(default, skip_serializing_if = "Option::is_none")
)]
pub published_at: Option<u64>,
- #[cfg_attr(all(feature = "serde", not(feature = "dto-bindgen")), serde(default))]
+ #[cfg_attr(feature = "serde", serde(default))]
pub farm: RadrootsFarmRef,
pub product: RadrootsListingProduct,
pub primary_bin_id: RadrootsInventoryBinId,
@@ -198,4 +198,45 @@ mod tests {
assert_eq!(listing.published_at, Some(1_700_000_000));
assert!(is_listing_kind(KIND_LISTING_DRAFT));
}
+
+ #[test]
+ #[cfg(feature = "serde")]
+ fn listing_deserializes_missing_farm_to_default_ref() {
+ let listing = super::RadrootsListing {
+ d_tag: "listing-draft".parse().unwrap(),
+ published_at: Some(1_700_000_000),
+ farm: RadrootsFarmRef {
+ pubkey: "farm-pubkey".to_string(),
+ d_tag: "farm-d-tag".to_string(),
+ },
+ product: super::RadrootsListingProduct {
+ key: "lettuce".to_string(),
+ title: "lettuce".to_string(),
+ category: "produce".to_string(),
+ summary: None,
+ process: None,
+ lot: None,
+ location: None,
+ profile: None,
+ year: None,
+ },
+ primary_bin_id: "bin-1".parse().unwrap(),
+ bins: vec![],
+ resource_area: None,
+ plot: None,
+ discounts: None,
+ inventory_available: None,
+ availability: None,
+ delivery_method: None,
+ location: None,
+ images: None,
+ };
+ let mut json = serde_json::to_value(&listing).unwrap();
+ json.as_object_mut().unwrap().remove("farm");
+
+ let parsed: super::RadrootsListing = serde_json::from_value(json).unwrap();
+
+ assert!(parsed.farm.pubkey.is_empty());
+ assert!(parsed.farm.d_tag.is_empty());
+ }
}
diff --git a/crates/events/src/order.rs b/crates/events/src/order.rs
@@ -24,7 +24,6 @@ pub const RADROOTS_ORDER_ENVELOPE_VERSION: u16 = 1;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(feature = "serde", serde(tag = "kind", content = "amount"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RadrootsListingParseError {
InvalidKind(u32),
@@ -230,10 +229,7 @@ impl RadrootsOrderRevisionProposal {
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "dto-bindgen", dto(export))]
-#[cfg_attr(
- feature = "serde",
- serde(rename_all = "snake_case", tag = "decision", content = "amount")
-)]
+#[cfg_attr(feature = "serde", serde(rename_all = "snake_case", tag = "decision"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RadrootsOrderRevisionOutcome {
Accepted,
@@ -1231,6 +1227,61 @@ mod tests {
}
#[test]
+ fn listing_parse_error_json_preserves_external_tagged_shape() {
+ assert_eq!(
+ serde_json::to_value(RadrootsListingParseError::InvalidKind(KIND_PROFILE)).unwrap(),
+ serde_json::json!({ "InvalidKind": KIND_PROFILE })
+ );
+ assert_eq!(
+ serde_json::to_value(RadrootsListingParseError::MissingTag("price".into())).unwrap(),
+ serde_json::json!({ "MissingTag": "price" })
+ );
+ assert_eq!(
+ serde_json::to_value(RadrootsListingParseError::InvalidUnit).unwrap(),
+ serde_json::json!("InvalidUnit")
+ );
+ assert_eq!(
+ serde_json::from_value::<RadrootsListingParseError>(serde_json::json!({
+ "InvalidJson": "bins"
+ }))
+ .unwrap(),
+ RadrootsListingParseError::InvalidJson("bins".into())
+ );
+ }
+
+ #[test]
+ fn order_revision_outcome_json_preserves_internal_tagged_shape() {
+ assert_eq!(
+ serde_json::to_value(RadrootsOrderRevisionOutcome::Accepted).unwrap(),
+ serde_json::json!({ "decision": "accepted" })
+ );
+ assert_eq!(
+ serde_json::to_value(RadrootsOrderRevisionOutcome::Declined {
+ reason: "out of stock".into(),
+ })
+ .unwrap(),
+ serde_json::json!({ "decision": "declined", "reason": "out of stock" })
+ );
+ assert_eq!(
+ serde_json::from_value::<RadrootsOrderRevisionOutcome>(serde_json::json!({
+ "decision": "accepted"
+ }))
+ .unwrap(),
+ RadrootsOrderRevisionOutcome::Accepted
+ );
+ assert_eq!(
+ serde_json::from_value::<RadrootsOrderRevisionOutcome>(serde_json::json!({
+ "decision": "declined",
+ "reason": "out of stock"
+ }))
+ .unwrap(),
+ RadrootsOrderRevisionOutcome::Declined {
+ reason: "out of stock".into()
+ }
+ );
+ }
+
+ #[test]
fn order_economics_validation_accepts_canonical_totals() {
let economics = sample_order_economics();
assert_eq!(economics.validate(), Ok(()));