lib

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

commit a28aa10587e11055d7d1e99b1af880593ae3ddcf
parent d44eb0fe99ed4d8f6e8f3948d3f980c77a64e2b1
Author: triesap <tyson@radroots.org>
Date:   Sat, 27 Jun 2026 20:34:38 +0000

trade: expose workflow state DTO

- export RadrootsTradeWorkflowState as a dto_bindgen root
- pin workflow state serde names to approved snake_case wires
- add DTO registry coverage for workflow state discriminants
- verify events and trade all-features check, tests, and clippy

Diffstat:
Mcrates/trade/src/dto.rs | 47+++++++++++++++++++++++++++++++++++++++++++++--
Mcrates/trade/src/workflow.rs | 32+++++++++++++++++++++++++++++++-
2 files changed, 76 insertions(+), 3 deletions(-)

diff --git a/crates/trade/src/dto.rs b/crates/trade/src/dto.rs @@ -7,12 +7,14 @@ use crate::listing::{ model::{RadrootsTradeListingSubtotal, RadrootsTradeListingTotal}, validation::RadrootsTradeListing, }; +use crate::workflow::RadrootsTradeWorkflowState; -pub fn dto_roots() -> [RootDescriptor; 3] { +pub fn dto_roots() -> [RootDescriptor; 4] { [ RootDescriptor::new::<RadrootsTradeListing>(), RootDescriptor::new::<RadrootsTradeListingSubtotal>(), RootDescriptor::new::<RadrootsTradeListingTotal>(), + RootDescriptor::new::<RadrootsTradeWorkflowState>(), ] } @@ -219,7 +221,9 @@ fn span(file: &str, line: u32) -> SourceSpan { #[cfg(test)] mod tests { - use dto_bindgen_core::{BackendId, Registry, StructDef, TypeDef, TypeRef, build_registry}; + use dto_bindgen_core::{ + BackendId, EnumDef, Registry, StructDef, TypeDef, TypeRef, build_registry, + }; use super::dto_roots; @@ -227,6 +231,7 @@ mod tests { "RadrootsTradeListing", "RadrootsTradeListingSubtotal", "RadrootsTradeListingTotal", + "RadrootsTradeWorkflowState", ]; #[test] @@ -268,6 +273,26 @@ mod tests { ); } + #[test] + fn trade_workflow_state_uses_approved_wire_vocabulary() { + let registry = build_registry(dto_roots()); + let workflow_state = find_enum(&registry, "RadrootsTradeWorkflowState"); + + assert_eq!( + enum_wire_names(workflow_state), + [ + "missing", + "requested", + "revision_proposed", + "agreed_pending_rhi", + "committed", + "declined", + "cancelled", + "invalid", + ] + ); + } + fn root_export_names(registry: &Registry) -> Vec<&str> { registry .roots @@ -299,6 +324,24 @@ mod tests { .expect("descriptor struct") } + fn find_enum<'a>(registry: &'a Registry, export_name: &str) -> &'a EnumDef { + registry + .types_by_id + .values() + .find_map(|def| match def { + TypeDef::Enum(def) if def.export_name == export_name => Some(def), + _ => None, + }) + .expect("descriptor enum") + } + + fn enum_wire_names(def: &EnumDef) -> Vec<&str> { + def.variants + .iter() + .map(|variant| variant.wire_name.as_str()) + .collect() + } + fn field_ty<'a>(def: &'a StructDef, field_name: &str) -> &'a TypeRef { &def.fields .iter() diff --git a/crates/trade/src/workflow.rs b/crates/trade/src/workflow.rs @@ -18,8 +18,11 @@ use crate::validation_receipt::{ RadrootsValidationReceiptType, }; -#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))] +#[cfg_attr(feature = "dto-bindgen", dto(export))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum RadrootsTradeWorkflowState { Missing, Requested, @@ -296,6 +299,33 @@ mod tests { const BUYER: &str = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; const SELLER: &str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + #[cfg(feature = "serde_json")] + #[test] + fn workflow_state_serializes_as_approved_wire_vocabulary() { + for (state, wire_name) in [ + (RadrootsTradeWorkflowState::Missing, "missing"), + (RadrootsTradeWorkflowState::Requested, "requested"), + ( + RadrootsTradeWorkflowState::RevisionProposed, + "revision_proposed", + ), + ( + RadrootsTradeWorkflowState::AgreedPendingRhi, + "agreed_pending_rhi", + ), + (RadrootsTradeWorkflowState::Committed, "committed"), + (RadrootsTradeWorkflowState::Declined, "declined"), + (RadrootsTradeWorkflowState::Cancelled, "cancelled"), + (RadrootsTradeWorkflowState::Invalid, "invalid"), + ] { + assert_eq!(serde_json::to_value(&state).unwrap(), wire_name); + assert_eq!( + serde_json::from_value::<RadrootsTradeWorkflowState>(wire_name.into()).unwrap(), + state + ); + } + } + fn event_id(raw: u8) -> RadrootsEventId { RadrootsEventId::parse(format!("{raw:064x}")).expect("event id") }