commit 9364af59dc39a4b25021c0d9fb33da30499ef832
parent e3859f4d4b41f1aa3d396f1f378060f704719573
Author: triesap <tyson@radroots.org>
Date: Wed, 24 Jun 2026 08:30:26 +0000
trade: inventory dto source roots
Diffstat:
6 files changed, 127 insertions(+), 0 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -4820,6 +4820,8 @@ name = "radroots_trade"
version = "0.1.0-alpha.2"
dependencies = [
"base64 0.22.1",
+ "dto_bindgen",
+ "dto_bindgen_core",
"hex",
"radroots_authority",
"radroots_core",
diff --git a/crates/trade/Cargo.toml b/crates/trade/Cargo.toml
@@ -14,6 +14,14 @@ readme = "README"
[features]
default = ["std", "serde", "serde_json"]
+dto-bindgen = [
+ "std",
+ "serde_json",
+ "dep:dto_bindgen",
+ "dep:dto_bindgen_core",
+ "radroots_core/dto-bindgen",
+ "radroots_events/dto-bindgen",
+]
std = [
"radroots_authority/std",
"radroots_core/std",
@@ -49,6 +57,8 @@ radroots_events = { workspace = true, default-features = false }
radroots_events_codec = { workspace = true, default-features = false }
radroots_event_store = { workspace = true, optional = true, default-features = false }
base64 = { workspace = true, optional = true }
+dto_bindgen = { workspace = true, optional = true }
+dto_bindgen_core = { workspace = true, optional = true }
hex = { workspace = true, optional = true }
serde = { workspace = true, default-features = false, features = [
"alloc",
diff --git a/crates/trade/src/dto.rs b/crates/trade/src/dto.rs
@@ -0,0 +1,110 @@
+use dto_bindgen_core::RootDescriptor;
+
+use crate::listing::{
+ model::{RadrootsTradeListingSubtotal, RadrootsTradeListingTotal},
+ validation::RadrootsTradeListing,
+};
+
+pub fn dto_roots() -> [RootDescriptor; 3] {
+ [
+ RootDescriptor::new::<RadrootsTradeListing>(),
+ RootDescriptor::new::<RadrootsTradeListingSubtotal>(),
+ RootDescriptor::new::<RadrootsTradeListingTotal>(),
+ ]
+}
+
+#[cfg(test)]
+mod tests {
+ use dto_bindgen_core::{Registry, TypeDef, build_registry};
+
+ use super::dto_roots;
+
+ const TRADE_SOURCE_ROOTS: &[&str] = &[
+ "RadrootsTradeListing",
+ "RadrootsTradeListingSubtotal",
+ "RadrootsTradeListingTotal",
+ ];
+ const TRADE_IMPORTED_SOURCE_DEPENDENCIES: &[&str] = &[
+ "RadrootsCoreDecimal",
+ "RadrootsCoreDiscount",
+ "RadrootsCoreDiscountScope",
+ "RadrootsCoreDiscountThreshold",
+ "RadrootsCoreDiscountValue",
+ "RadrootsCoreMoney",
+ "RadrootsCorePercent",
+ "RadrootsCoreQuantity",
+ "RadrootsCoreQuantityPrice",
+ "RadrootsCoreUnit",
+ "RadrootsFarmRef",
+ "RadrootsListing",
+ "RadrootsListingAvailability",
+ "RadrootsListingBin",
+ "RadrootsListingDeliveryMethod",
+ "RadrootsListingImage",
+ "RadrootsListingImageSize",
+ "RadrootsListingLocation",
+ "RadrootsListingProduct",
+ "RadrootsListingStatus",
+ "RadrootsPlotRef",
+ "RadrootsResourceAreaRef",
+ ];
+
+ #[test]
+ fn trade_descriptor_roots_build_registry() {
+ let registry = build_registry(dto_roots());
+
+ assert!(
+ !registry.has_errors(),
+ "trade registry has diagnostics: {:?}",
+ registry.diagnostics
+ );
+ assert_eq!(registry.roots.len(), dto_roots().len());
+ }
+
+ #[test]
+ fn trade_source_roots_are_deterministic() {
+ let registry = build_registry(dto_roots());
+
+ assert_eq!(root_export_names(®istry), TRADE_SOURCE_ROOTS);
+ }
+
+ #[test]
+ fn trade_source_dependencies_are_explicit() {
+ let registry = build_registry(dto_roots());
+ let mut dependencies = type_export_names(®istry)
+ .into_iter()
+ .filter(|name| !TRADE_SOURCE_ROOTS.contains(name))
+ .collect::<Vec<_>>();
+ dependencies.sort();
+
+ assert_eq!(dependencies, TRADE_IMPORTED_SOURCE_DEPENDENCIES);
+ }
+
+ fn root_export_names(registry: &Registry) -> Vec<&str> {
+ registry
+ .roots
+ .iter()
+ .map(|type_id| {
+ registry
+ .type_def(*type_id)
+ .map(type_export_name)
+ .expect("root type")
+ })
+ .collect()
+ }
+
+ fn type_export_names(registry: &Registry) -> Vec<&str> {
+ registry
+ .types_by_id
+ .values()
+ .map(type_export_name)
+ .collect::<Vec<_>>()
+ }
+
+ fn type_export_name(def: &TypeDef) -> &str {
+ match def {
+ TypeDef::Struct(def) => def.export_name.as_str(),
+ TypeDef::Enum(def) => def.export_name.as_str(),
+ }
+ }
+}
diff --git a/crates/trade/src/lib.rs b/crates/trade/src/lib.rs
@@ -3,6 +3,8 @@
#[cfg(not(feature = "std"))]
extern crate alloc;
+#[cfg(feature = "dto-bindgen")]
+pub mod dto;
pub mod listing;
pub mod order;
pub mod prelude;
diff --git a/crates/trade/src/listing/model.rs b/crates/trade/src/listing/model.rs
@@ -1,3 +1,4 @@
+#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsTradeListingSubtotal {
@@ -7,6 +8,7 @@ pub struct RadrootsTradeListingSubtotal {
pub quantity_unit: radroots_core::RadrootsCoreUnit,
}
+#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RadrootsTradeListingTotal {
diff --git a/crates/trade/src/listing/validation.rs b/crates/trade/src/listing/validation.rs
@@ -20,6 +20,7 @@ use radroots_events::{
use crate::listing::codec::listing_from_event_parts;
+#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug)]
pub struct RadrootsTradeListing {