commit e8c3c6c886ce94a8b989b099396b65e57da82e07
parent 86dba23e631c9cc50d3cf03785e16cdd14150a4e
Author: triesap <tyson@radroots.org>
Date: Wed, 24 Jun 2026 06:25:18 +0000
dto: add source descriptor features
Diffstat:
8 files changed, 727 insertions(+), 0 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -1500,6 +1500,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
[[package]]
+name = "dto_bindgen_core"
+version = "0.1.0"
+source = "git+https://github.com/triesap/dto_bindgen?rev=96ed6c691aacab31860828d25da2e0167b13d92c#96ed6c691aacab31860828d25da2e0167b13d92c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "serde",
+ "serde_json",
+ "sha2",
+ "syn 2.0.117",
+ "toml",
+]
+
+[[package]]
name = "dynasm"
version = "3.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4222,6 +4236,7 @@ dependencies = [
name = "radroots_core"
version = "0.1.0-alpha.2"
dependencies = [
+ "dto_bindgen_core",
"rust_decimal",
"rust_decimal_macros",
"serde",
@@ -4246,6 +4261,7 @@ dependencies = [
name = "radroots_events"
version = "0.1.0-alpha.2"
dependencies = [
+ "dto_bindgen_core",
"hex",
"radroots_core",
"serde",
diff --git a/Cargo.toml b/Cargo.toml
@@ -58,6 +58,7 @@ homepage = "https://radroots.org"
readme = "README"
[workspace.dependencies]
+dto_bindgen_core = { git = "https://github.com/triesap/dto_bindgen", rev = "96ed6c691aacab31860828d25da2e0167b13d92c", 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/core/Cargo.toml b/crates/core/Cargo.toml
@@ -14,10 +14,12 @@ readme = "README"
[features]
default = ["std", "serde"]
+dto-bindgen = ["std", "serde", "dep:dto_bindgen_core"]
std = []
serde = ["dep:serde", "rust_decimal/serde"]
[dependencies]
+dto_bindgen_core = { workspace = true, optional = true }
rust_decimal = { workspace = true, default-features = false }
rust_decimal_macros = { workspace = true }
serde = { workspace = true, default-features = false, features = [
diff --git a/crates/core/src/dto.rs b/crates/core/src/dto.rs
@@ -0,0 +1,375 @@
+use dto_bindgen_core::{
+ DescribeCtx, Dto, EnumDef, EnumRepr, FieldDef, FieldPresence, IdentName, RootDescriptor,
+ RustTypeId, SourceSpan, StructDef, TargetFieldNames, TypeDef, TypeRef, VariantDef,
+ VariantShape, WireFieldNames,
+};
+
+use crate::{
+ RadrootsCoreCurrency, RadrootsCoreDecimal, RadrootsCoreDiscount, RadrootsCoreDiscountScope,
+ RadrootsCoreDiscountThreshold, RadrootsCoreDiscountValue, RadrootsCoreMoney,
+ RadrootsCorePercent, RadrootsCoreQuantity, RadrootsCoreQuantityPrice, RadrootsCoreUnit,
+ RadrootsCoreUnitDimension,
+};
+
+pub fn dto_roots() -> [RootDescriptor; 9] {
+ [
+ RootDescriptor::new::<RadrootsCoreDiscount>(),
+ RootDescriptor::new::<RadrootsCoreDiscountScope>(),
+ RootDescriptor::new::<RadrootsCoreDiscountThreshold>(),
+ RootDescriptor::new::<RadrootsCoreDiscountValue>(),
+ RootDescriptor::new::<RadrootsCoreMoney>(),
+ RootDescriptor::new::<RadrootsCorePercent>(),
+ RootDescriptor::new::<RadrootsCoreQuantity>(),
+ RootDescriptor::new::<RadrootsCoreQuantityPrice>(),
+ RootDescriptor::new::<RadrootsCoreUnit>(),
+ ]
+}
+
+impl Dto for RadrootsCoreCurrency {
+ fn describe(_ctx: &mut DescribeCtx) -> TypeRef {
+ TypeRef::String
+ }
+}
+
+impl Dto for RadrootsCoreDecimal {
+ fn describe(_ctx: &mut DescribeCtx) -> TypeRef {
+ TypeRef::String
+ }
+}
+
+impl Dto for RadrootsCoreUnitDimension {
+ fn describe(ctx: &mut DescribeCtx) -> TypeRef {
+ let def = EnumDef::new(
+ "RadrootsCoreUnitDimension",
+ "RadrootsCoreUnitDimension",
+ EnumRepr::External,
+ span("crates/core/src/unit.rs", 17),
+ )
+ .with_variant(unit_variant(
+ "Count",
+ "count",
+ "crates/core/src/unit.rs",
+ 18,
+ ))
+ .with_variant(unit_variant("Mass", "mass", "crates/core/src/unit.rs", 19))
+ .with_variant(unit_variant(
+ "Volume",
+ "volume",
+ "crates/core/src/unit.rs",
+ 20,
+ ));
+ register(ctx, "RadrootsCoreUnitDimension", TypeDef::Enum(def))
+ }
+}
+
+impl Dto for RadrootsCoreUnit {
+ fn describe(ctx: &mut DescribeCtx) -> TypeRef {
+ let def = EnumDef::new(
+ "RadrootsCoreUnit",
+ "RadrootsCoreUnit",
+ EnumRepr::External,
+ span("crates/core/src/unit.rs", 24),
+ )
+ .with_variant(unit_variant("Each", "each", "crates/core/src/unit.rs", 25))
+ .with_variant(unit_variant("MassKg", "kg", "crates/core/src/unit.rs", 26))
+ .with_variant(unit_variant("MassG", "g", "crates/core/src/unit.rs", 27))
+ .with_variant(unit_variant("MassOz", "oz", "crates/core/src/unit.rs", 28))
+ .with_variant(unit_variant("MassLb", "lb", "crates/core/src/unit.rs", 29))
+ .with_variant(unit_variant("VolumeL", "l", "crates/core/src/unit.rs", 30))
+ .with_variant(unit_variant(
+ "VolumeMl",
+ "ml",
+ "crates/core/src/unit.rs",
+ 31,
+ ));
+ register(ctx, "RadrootsCoreUnit", TypeDef::Enum(def))
+ }
+}
+
+impl Dto for RadrootsCoreMoney {
+ fn describe(ctx: &mut DescribeCtx) -> TypeRef {
+ let def = StructDef::new(
+ "RadrootsCoreMoney",
+ "RadrootsCoreMoney",
+ span("crates/core/src/money.rs", 8),
+ )
+ .with_field(field(
+ "amount",
+ "amount",
+ RadrootsCoreDecimal::describe(ctx),
+ "crates/core/src/money.rs",
+ 9,
+ ))
+ .with_field(field(
+ "currency",
+ "currency",
+ RadrootsCoreCurrency::describe(ctx),
+ "crates/core/src/money.rs",
+ 10,
+ ));
+ register(ctx, "RadrootsCoreMoney", TypeDef::Struct(def))
+ }
+}
+
+impl Dto for RadrootsCorePercent {
+ fn describe(ctx: &mut DescribeCtx) -> TypeRef {
+ let def = StructDef::new(
+ "RadrootsCorePercent",
+ "RadrootsCorePercent",
+ span("crates/core/src/percent.rs", 9),
+ )
+ .with_field(field(
+ "value",
+ "value",
+ RadrootsCoreDecimal::describe(ctx),
+ "crates/core/src/percent.rs",
+ 10,
+ ));
+ register(ctx, "RadrootsCorePercent", TypeDef::Struct(def))
+ }
+}
+
+impl Dto for RadrootsCoreQuantity {
+ fn describe(ctx: &mut DescribeCtx) -> TypeRef {
+ let unit = RadrootsCoreUnit::describe(ctx);
+ let def = StructDef::new(
+ "RadrootsCoreQuantity",
+ "RadrootsCoreQuantity",
+ span("crates/core/src/quantity.rs", 13),
+ )
+ .with_field(field(
+ "amount",
+ "amount",
+ RadrootsCoreDecimal::describe(ctx),
+ "crates/core/src/quantity.rs",
+ 14,
+ ))
+ .with_field(field(
+ "unit",
+ "unit",
+ unit,
+ "crates/core/src/quantity.rs",
+ 16,
+ ))
+ .with_field(
+ field(
+ "label",
+ "label",
+ <Option<String> as Dto>::describe(ctx),
+ "crates/core/src/quantity.rs",
+ 18,
+ )
+ .with_presence(FieldPresence::optional_nullable_skip_if_none()),
+ );
+ register(ctx, "RadrootsCoreQuantity", TypeDef::Struct(def))
+ }
+}
+
+impl Dto for RadrootsCoreQuantityPrice {
+ fn describe(ctx: &mut DescribeCtx) -> TypeRef {
+ let amount = RadrootsCoreMoney::describe(ctx);
+ let quantity = RadrootsCoreQuantity::describe(ctx);
+ let def = StructDef::new(
+ "RadrootsCoreQuantityPrice",
+ "RadrootsCoreQuantityPrice",
+ span("crates/core/src/quantity_price.rs", 5),
+ )
+ .with_field(field(
+ "amount",
+ "amount",
+ amount,
+ "crates/core/src/quantity_price.rs",
+ 7,
+ ))
+ .with_field(field(
+ "quantity",
+ "quantity",
+ quantity,
+ "crates/core/src/quantity_price.rs",
+ 9,
+ ));
+ register(ctx, "RadrootsCoreQuantityPrice", TypeDef::Struct(def))
+ }
+}
+
+impl Dto for RadrootsCoreDiscountScope {
+ fn describe(ctx: &mut DescribeCtx) -> TypeRef {
+ let def = EnumDef::new(
+ "RadrootsCoreDiscountScope",
+ "RadrootsCoreDiscountScope",
+ EnumRepr::External,
+ span("crates/core/src/discount.rs", 9),
+ )
+ .with_variant(unit_variant(
+ "Bin",
+ "bin",
+ "crates/core/src/discount.rs",
+ 10,
+ ))
+ .with_variant(unit_variant(
+ "OrderTotal",
+ "order_total",
+ "crates/core/src/discount.rs",
+ 11,
+ ));
+ register(ctx, "RadrootsCoreDiscountScope", TypeDef::Enum(def))
+ }
+}
+
+impl Dto for RadrootsCoreDiscountThreshold {
+ fn describe(ctx: &mut DescribeCtx) -> TypeRef {
+ let quantity = RadrootsCoreQuantity::describe(ctx);
+ let def = EnumDef::new(
+ "RadrootsCoreDiscountThreshold",
+ "RadrootsCoreDiscountThreshold",
+ EnumRepr::Adjacent {
+ tag: "kind".to_owned(),
+ content: "amount".to_owned(),
+ },
+ span("crates/core/src/discount.rs", 20),
+ )
+ .with_variant(VariantDef::new(
+ "BinCount",
+ "bin_count",
+ VariantShape::Struct(vec![
+ field(
+ "bin_id",
+ "bin_id",
+ String::describe(ctx),
+ "crates/core/src/discount.rs",
+ 21,
+ ),
+ field(
+ "min",
+ "min",
+ u32::describe(ctx),
+ "crates/core/src/discount.rs",
+ 21,
+ ),
+ ]),
+ span("crates/core/src/discount.rs", 21),
+ ))
+ .with_variant(VariantDef::new(
+ "OrderQuantity",
+ "order_quantity",
+ VariantShape::Struct(vec![field(
+ "min",
+ "min",
+ quantity,
+ "crates/core/src/discount.rs",
+ 22,
+ )]),
+ span("crates/core/src/discount.rs", 22),
+ ));
+ register(ctx, "RadrootsCoreDiscountThreshold", TypeDef::Enum(def))
+ }
+}
+
+impl Dto for RadrootsCoreDiscountValue {
+ fn describe(ctx: &mut DescribeCtx) -> TypeRef {
+ let money = RadrootsCoreMoney::describe(ctx);
+ let percent = RadrootsCorePercent::describe(ctx);
+ let def = EnumDef::new(
+ "RadrootsCoreDiscountValue",
+ "RadrootsCoreDiscountValue",
+ EnumRepr::Adjacent {
+ tag: "kind".to_owned(),
+ content: "amount".to_owned(),
+ },
+ span("crates/core/src/discount.rs", 31),
+ )
+ .with_variant(VariantDef::new(
+ "MoneyPerBin",
+ "money_per_bin",
+ VariantShape::Newtype(money),
+ span("crates/core/src/discount.rs", 32),
+ ))
+ .with_variant(VariantDef::new(
+ "Percent",
+ "percent",
+ VariantShape::Newtype(percent),
+ span("crates/core/src/discount.rs", 33),
+ ));
+ register(ctx, "RadrootsCoreDiscountValue", TypeDef::Enum(def))
+ }
+}
+
+impl Dto for RadrootsCoreDiscount {
+ fn describe(ctx: &mut DescribeCtx) -> TypeRef {
+ let scope = RadrootsCoreDiscountScope::describe(ctx);
+ let threshold = RadrootsCoreDiscountThreshold::describe(ctx);
+ let value = RadrootsCoreDiscountValue::describe(ctx);
+ let def = StructDef::new(
+ "RadrootsCoreDiscount",
+ "RadrootsCoreDiscount",
+ span("crates/core/src/discount.rs", 39),
+ )
+ .with_field(field(
+ "scope",
+ "scope",
+ scope,
+ "crates/core/src/discount.rs",
+ 40,
+ ))
+ .with_field(field(
+ "threshold",
+ "threshold",
+ threshold,
+ "crates/core/src/discount.rs",
+ 41,
+ ))
+ .with_field(field(
+ "value",
+ "value",
+ value,
+ "crates/core/src/discount.rs",
+ 42,
+ ));
+ register(ctx, "RadrootsCoreDiscount", TypeDef::Struct(def))
+ }
+}
+
+fn register(ctx: &mut DescribeCtx, rust_ident: &str, type_def: TypeDef) -> TypeRef {
+ ctx.register_type(RustTypeId::new("radroots_core", rust_ident), type_def)
+}
+
+fn unit_variant(rust_name: &str, wire_name: &str, file: &str, line: u32) -> VariantDef {
+ VariantDef::new(rust_name, wire_name, VariantShape::Unit, span(file, line))
+}
+
+fn field(rust_name: &str, wire_name: &str, ty: TypeRef, file: &str, line: u32) -> FieldDef {
+ FieldDef::new(
+ IdentName::new(rust_name),
+ WireFieldNames::same(wire_name),
+ TargetFieldNames::new(wire_name, rust_name),
+ ty,
+ span(file, line),
+ )
+}
+
+fn span(file: &str, line: u32) -> SourceSpan {
+ SourceSpan::new(file, line, 1)
+}
+
+#[cfg(test)]
+mod tests {
+ use dto_bindgen_core::{TypeDef, build_registry};
+
+ use super::dto_roots;
+
+ #[test]
+ fn core_descriptor_roots_build_registry() {
+ let registry = build_registry(dto_roots());
+
+ assert!(!registry.has_errors());
+ assert_eq!(registry.roots.len(), dto_roots().len());
+ assert!(registry.types_by_id.values().any(
+ |def| matches!(def, TypeDef::Struct(def) if def.export_name == "RadrootsCoreMoney")
+ ));
+ assert!(
+ registry.types_by_id.values().any(
+ |def| matches!(def, TypeDef::Enum(def) if def.export_name == "RadrootsCoreUnit")
+ )
+ );
+ }
+}
diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs
@@ -6,6 +6,8 @@ extern crate alloc;
pub mod currency;
pub mod decimal;
pub mod discount;
+#[cfg(feature = "dto-bindgen")]
+pub mod dto;
pub mod money;
pub mod percent;
pub mod quantity;
diff --git a/crates/events/Cargo.toml b/crates/events/Cargo.toml
@@ -14,10 +14,12 @@ readme = "README"
[features]
default = ["std", "serde"]
+dto-bindgen = ["std", "serde", "dep:dto_bindgen_core", "radroots_core/dto-bindgen"]
std = ["radroots_core/std"]
serde = ["dep:serde", "radroots_core/serde"]
[dependencies]
+dto_bindgen_core = { workspace = true, optional = true }
radroots_core = { workspace = true, default-features = false }
hex = { version = "0.4", default-features = false, features = ["alloc"] }
serde = { workspace = true, default-features = false, features = [
diff --git a/crates/events/src/dto.rs b/crates/events/src/dto.rs
@@ -0,0 +1,327 @@
+use dto_bindgen_core::{
+ DescribeCtx, Dto, FieldDef, FieldPresence, IdentName, RootDescriptor, RustTypeId, SourceSpan,
+ StructDef, TargetFieldNames, TypeDef, TypeRef, WireFieldNames,
+};
+
+use crate::{
+ RadrootsNostrEvent, RadrootsNostrEventPtr, RadrootsNostrEventRef,
+ listing::{RadrootsListingImage, RadrootsListingImageSize, RadrootsListingProduct},
+};
+
+pub fn dto_roots() -> [RootDescriptor; 5] {
+ [
+ RootDescriptor::new::<RadrootsNostrEvent>(),
+ RootDescriptor::new::<RadrootsNostrEventRef>(),
+ RootDescriptor::new::<RadrootsNostrEventPtr>(),
+ RootDescriptor::new::<RadrootsListingProduct>(),
+ RootDescriptor::new::<RadrootsListingImage>(),
+ ]
+}
+
+impl Dto for RadrootsNostrEvent {
+ fn describe(ctx: &mut DescribeCtx) -> TypeRef {
+ let def = StructDef::new(
+ "RadrootsNostrEvent",
+ "RadrootsNostrEvent",
+ span("crates/events/src/lib.rs", 52),
+ )
+ .with_field(field(
+ "id",
+ "id",
+ String::describe(ctx),
+ "crates/events/src/lib.rs",
+ 53,
+ ))
+ .with_field(field(
+ "author",
+ "author",
+ String::describe(ctx),
+ "crates/events/src/lib.rs",
+ 54,
+ ))
+ .with_field(field(
+ "created_at",
+ "created_at",
+ u32::describe(ctx),
+ "crates/events/src/lib.rs",
+ 55,
+ ))
+ .with_field(field(
+ "kind",
+ "kind",
+ u32::describe(ctx),
+ "crates/events/src/lib.rs",
+ 56,
+ ))
+ .with_field(field(
+ "tags",
+ "tags",
+ <Vec<Vec<String>> as Dto>::describe(ctx),
+ "crates/events/src/lib.rs",
+ 57,
+ ))
+ .with_field(field(
+ "content",
+ "content",
+ String::describe(ctx),
+ "crates/events/src/lib.rs",
+ 58,
+ ))
+ .with_field(field(
+ "sig",
+ "sig",
+ String::describe(ctx),
+ "crates/events/src/lib.rs",
+ 59,
+ ));
+ register(ctx, "RadrootsNostrEvent", TypeDef::Struct(def))
+ }
+}
+
+impl Dto for RadrootsNostrEventRef {
+ fn describe(ctx: &mut DescribeCtx) -> TypeRef {
+ let def = StructDef::new(
+ "RadrootsNostrEventRef",
+ "RadrootsNostrEventRef",
+ span("crates/events/src/lib.rs", 64),
+ )
+ .with_field(field(
+ "id",
+ "id",
+ String::describe(ctx),
+ "crates/events/src/lib.rs",
+ 65,
+ ))
+ .with_field(field(
+ "author",
+ "author",
+ String::describe(ctx),
+ "crates/events/src/lib.rs",
+ 66,
+ ))
+ .with_field(field(
+ "kind",
+ "kind",
+ u32::describe(ctx),
+ "crates/events/src/lib.rs",
+ 67,
+ ))
+ .with_field(field(
+ "d_tag",
+ "d_tag",
+ <Option<String> as Dto>::describe(ctx),
+ "crates/events/src/lib.rs",
+ 68,
+ ))
+ .with_field(field(
+ "relays",
+ "relays",
+ <Option<Vec<String>> as Dto>::describe(ctx),
+ "crates/events/src/lib.rs",
+ 69,
+ ));
+ register(ctx, "RadrootsNostrEventRef", TypeDef::Struct(def))
+ }
+}
+
+impl Dto for RadrootsNostrEventPtr {
+ fn describe(ctx: &mut DescribeCtx) -> TypeRef {
+ let def = StructDef::new(
+ "RadrootsNostrEventPtr",
+ "RadrootsNostrEventPtr",
+ span("crates/events/src/lib.rs", 74),
+ )
+ .with_field(field(
+ "id",
+ "id",
+ String::describe(ctx),
+ "crates/events/src/lib.rs",
+ 75,
+ ))
+ .with_field(field(
+ "relays",
+ "relays",
+ <Option<String> as Dto>::describe(ctx),
+ "crates/events/src/lib.rs",
+ 76,
+ ));
+ register(ctx, "RadrootsNostrEventPtr", TypeDef::Struct(def))
+ }
+}
+
+impl Dto for RadrootsListingProduct {
+ fn describe(ctx: &mut DescribeCtx) -> TypeRef {
+ let def = StructDef::new(
+ "RadrootsListingProduct",
+ "RadrootsListingProduct",
+ span("crates/events/src/listing.rs", 81),
+ )
+ .with_field(field(
+ "key",
+ "key",
+ String::describe(ctx),
+ "crates/events/src/listing.rs",
+ 82,
+ ))
+ .with_field(field(
+ "title",
+ "title",
+ String::describe(ctx),
+ "crates/events/src/listing.rs",
+ 83,
+ ))
+ .with_field(field(
+ "category",
+ "category",
+ String::describe(ctx),
+ "crates/events/src/listing.rs",
+ 84,
+ ))
+ .with_field(nullable_field(
+ "summary",
+ "summary",
+ <Option<String> as Dto>::describe(ctx),
+ "crates/events/src/listing.rs",
+ 85,
+ ))
+ .with_field(nullable_field(
+ "process",
+ "process",
+ <Option<String> as Dto>::describe(ctx),
+ "crates/events/src/listing.rs",
+ 86,
+ ))
+ .with_field(nullable_field(
+ "lot",
+ "lot",
+ <Option<String> as Dto>::describe(ctx),
+ "crates/events/src/listing.rs",
+ 87,
+ ))
+ .with_field(nullable_field(
+ "location",
+ "location",
+ <Option<String> as Dto>::describe(ctx),
+ "crates/events/src/listing.rs",
+ 88,
+ ))
+ .with_field(nullable_field(
+ "profile",
+ "profile",
+ <Option<String> as Dto>::describe(ctx),
+ "crates/events/src/listing.rs",
+ 89,
+ ))
+ .with_field(nullable_field(
+ "year",
+ "year",
+ <Option<String> as Dto>::describe(ctx),
+ "crates/events/src/listing.rs",
+ 90,
+ ));
+ register(ctx, "RadrootsListingProduct", TypeDef::Struct(def))
+ }
+}
+
+impl Dto for RadrootsListingImageSize {
+ fn describe(ctx: &mut DescribeCtx) -> TypeRef {
+ let def = StructDef::new(
+ "RadrootsListingImageSize",
+ "RadrootsListingImageSize",
+ span("crates/events/src/listing.rs", 133),
+ )
+ .with_field(field(
+ "w",
+ "w",
+ u32::describe(ctx),
+ "crates/events/src/listing.rs",
+ 134,
+ ))
+ .with_field(field(
+ "h",
+ "h",
+ u32::describe(ctx),
+ "crates/events/src/listing.rs",
+ 135,
+ ));
+ register(ctx, "RadrootsListingImageSize", TypeDef::Struct(def))
+ }
+}
+
+impl Dto for RadrootsListingImage {
+ fn describe(ctx: &mut DescribeCtx) -> TypeRef {
+ let size = RadrootsListingImageSize::describe(ctx);
+ let def = StructDef::new(
+ "RadrootsListingImage",
+ "RadrootsListingImage",
+ span("crates/events/src/listing.rs", 126),
+ )
+ .with_field(field(
+ "url",
+ "url",
+ String::describe(ctx),
+ "crates/events/src/listing.rs",
+ 127,
+ ))
+ .with_field(nullable_field(
+ "size",
+ "size",
+ TypeRef::option(size),
+ "crates/events/src/listing.rs",
+ 128,
+ ));
+ register(ctx, "RadrootsListingImage", TypeDef::Struct(def))
+ }
+}
+
+fn register(ctx: &mut DescribeCtx, rust_ident: &str, type_def: TypeDef) -> TypeRef {
+ ctx.register_type(RustTypeId::new("radroots_events", rust_ident), type_def)
+}
+
+fn nullable_field(
+ rust_name: &str,
+ wire_name: &str,
+ ty: TypeRef,
+ file: &str,
+ line: u32,
+) -> FieldDef {
+ field(rust_name, wire_name, ty, file, line).with_presence(FieldPresence::nullable_required())
+}
+
+fn field(rust_name: &str, wire_name: &str, ty: TypeRef, file: &str, line: u32) -> FieldDef {
+ FieldDef::new(
+ IdentName::new(rust_name),
+ WireFieldNames::same(wire_name),
+ TargetFieldNames::new(wire_name, rust_name),
+ ty,
+ span(file, line),
+ )
+}
+
+fn span(file: &str, line: u32) -> SourceSpan {
+ SourceSpan::new(file, line, 1)
+}
+
+#[cfg(test)]
+mod tests {
+ use dto_bindgen_core::{TypeDef, build_registry};
+
+ use super::dto_roots;
+
+ #[test]
+ fn event_descriptor_roots_build_registry() {
+ let registry = build_registry(dto_roots());
+
+ assert!(!registry.has_errors());
+ assert_eq!(registry.roots.len(), dto_roots().len());
+ assert!(registry.types_by_id.values().any(
+ |def| matches!(def, TypeDef::Struct(def) if def.export_name == "RadrootsNostrEvent")
+ ));
+ assert!(
+ registry
+ .types_by_id
+ .values()
+ .any(|def| matches!(def, TypeDef::Struct(def) if def.export_name == "RadrootsListingImageSize"))
+ );
+ }
+}
diff --git a/crates/events/src/lib.rs b/crates/events/src/lib.rs
@@ -15,6 +15,8 @@ pub mod contract;
pub mod coop;
pub mod document;
pub mod draft;
+#[cfg(feature = "dto-bindgen")]
+pub mod dto;
pub mod event_head;
pub mod farm;
pub mod farm_crdt;