lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

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:
MCargo.lock | 10+++++-----
MCargo.toml | 4++--
Mcrates/events/src/listing.rs | 43++++++++++++++++++++++++++++++++++++++++++-
Mcrates/events/src/order.rs | 61++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
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(()));