commit 54a92213c851bef0d107c4a06688dc97426ac5ce
parent 05c68dc8409efe6f552341a70bbe5fd10bedcd10
Author: triesap <tyson@radroots.org>
Date: Wed, 24 Jun 2026 08:51:45 +0000
types: render bindings from dto registry
- replace the types binding model dependency with dto registry roots
- describe generic result envelopes with explicit DTO descriptors
- route the types package through xtask DTO registry rendering
- verify cargo, xtask generation, and package TypeScript checks
Diffstat:
5 files changed, 136 insertions(+), 33 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -2157,7 +2157,7 @@ dependencies = [
name = "radroots_types_bindings"
version = "0.1.0"
dependencies = [
- "radroots_sdk_binding_model",
+ "dto_bindgen_core",
"radroots_types",
]
diff --git a/crates/types_bindings/Cargo.toml b/crates/types_bindings/Cargo.toml
@@ -9,5 +9,5 @@ homepage.workspace = true
publish = false
[dependencies]
-radroots_sdk_binding_model = { path = "../binding_model" }
+dto_bindgen_core = { workspace = true }
radroots_types = { workspace = true }
diff --git a/crates/types_bindings/src/lib.rs b/crates/types_bindings/src/lib.rs
@@ -1,40 +1,129 @@
pub use radroots_types as upstream;
-use radroots_sdk_binding_model as ts;
-
-pub fn types_module() -> ts::TsModule {
- ts::module(vec![
- ts::type_alias_params(
- "IError",
- &["T"],
- ts::object(vec![ts::field("err", ts::reference("T"))]),
- ),
- ts::type_alias_params(
- "IResult",
- &["T"],
- ts::object(vec![ts::field("result", ts::reference("T"))]),
- ),
- ts::type_alias_params(
- "IResultList",
- &["T"],
- ts::object(vec![ts::field("results", ts::array(ts::reference("T")))]),
- ),
- ts::type_alias(
- "IResultPass",
- ts::object(vec![ts::field("pass", ts::boolean())]),
- ),
- ])
+use dto_bindgen_core::{
+ DescribeCtx, Dto, FieldDef, GenericParam, IdentName, Primitive, RootDescriptor, RustTypeId,
+ SourceSpan, StructDef, TargetFieldNames, TypeDef, TypeRef, WireFieldNames,
+};
+
+struct IErrorDto;
+struct IResultDto;
+struct IResultListDto;
+struct IResultPassDto;
+
+pub fn dto_roots() -> Vec<RootDescriptor> {
+ vec![
+ RootDescriptor::new::<IErrorDto>(),
+ RootDescriptor::new::<IResultDto>(),
+ RootDescriptor::new::<IResultListDto>(),
+ RootDescriptor::new::<IResultPassDto>(),
+ ]
+}
+
+impl Dto for IErrorDto {
+ fn describe(ctx: &mut DescribeCtx) -> TypeRef {
+ ctx.register_type(
+ rust_id("IError"),
+ generic_struct("IError", "err", TypeRef::GenericParam("T".to_owned())),
+ )
+ }
+}
+
+impl Dto for IResultDto {
+ fn describe(ctx: &mut DescribeCtx) -> TypeRef {
+ ctx.register_type(
+ rust_id("IResult"),
+ generic_struct("IResult", "result", TypeRef::GenericParam("T".to_owned())),
+ )
+ }
+}
+
+impl Dto for IResultListDto {
+ fn describe(ctx: &mut DescribeCtx) -> TypeRef {
+ ctx.register_type(
+ rust_id("IResultList"),
+ generic_struct(
+ "IResultList",
+ "results",
+ TypeRef::vec(TypeRef::GenericParam("T".to_owned())),
+ ),
+ )
+ }
+}
+
+impl Dto for IResultPassDto {
+ fn describe(ctx: &mut DescribeCtx) -> TypeRef {
+ ctx.register_type(
+ rust_id("IResultPass"),
+ TypeDef::Struct(
+ StructDef::new("IResultPass", "IResultPass", source_span())
+ .with_field(field("pass", TypeRef::Primitive(Primitive::Bool))),
+ ),
+ )
+ }
+}
+
+fn generic_struct(export_name: &str, field_name: &str, field_type: TypeRef) -> TypeDef {
+ let mut def = StructDef::new(export_name, export_name, source_span())
+ .with_field(field(field_name, field_type));
+ def.generics.push(GenericParam::new("T"));
+ TypeDef::Struct(def)
+}
+
+fn field(name: &str, ty: TypeRef) -> FieldDef {
+ FieldDef::new(
+ IdentName::new(name),
+ WireFieldNames::same(name),
+ TargetFieldNames::new(name, name),
+ ty,
+ source_span(),
+ )
+}
+
+fn rust_id(name: &'static str) -> RustTypeId {
+ RustTypeId::new(env!("CARGO_PKG_NAME"), name)
+}
+
+fn source_span() -> SourceSpan {
+ SourceSpan::new(file!(), line!(), column!())
}
#[cfg(test)]
mod tests {
- use super::types_module;
+ use super::dto_roots;
+ use dto_bindgen_core::{TypeDef, TypeRef, build_registry};
+
+ #[test]
+ fn preserves_result_wrapper_roots() {
+ let registry = build_registry(dto_roots());
+ let actual = registry
+ .types_by_id
+ .values()
+ .map(|type_def| match type_def {
+ TypeDef::Struct(def) => def.export_name.as_str(),
+ TypeDef::Enum(def) => def.export_name.as_str(),
+ })
+ .collect::<Vec<_>>();
+
+ assert_eq!(actual, ["IError", "IResult", "IResultList", "IResultPass"]);
+ }
#[test]
- fn preserves_result_wrapper_exports() {
- let rendered = types_module().render();
- assert!(rendered.contains("export type IError"));
- assert!(rendered.contains("export type IResultList"));
- assert!(rendered.contains("export type IResultPass"));
+ fn preserves_generic_result_wrapper_fields() {
+ let registry = build_registry(dto_roots());
+ let result_list = registry
+ .types_by_id
+ .values()
+ .find_map(|type_def| match type_def {
+ TypeDef::Struct(def) if def.export_name == "IResultList" => Some(def),
+ _ => None,
+ })
+ .expect("IResultList descriptor");
+
+ assert_eq!(result_list.generics[0].name, "T");
+ assert_eq!(result_list.fields[0].target.typescript, "results");
+ assert_eq!(
+ result_list.fields[0].ty,
+ TypeRef::vec(TypeRef::GenericParam("T".to_owned()))
+ );
}
}
diff --git a/tools/xtask/src/dto_roots.rs b/tools/xtask/src/dto_roots.rs
@@ -49,6 +49,10 @@ pub const DTO_PACKAGE_ROOTS: &[DtoPackageRootSet] = &[
package_key: "trade",
roots: trade_roots,
},
+ DtoPackageRootSet {
+ package_key: "types",
+ roots: types_roots,
+ },
];
pub const MANUAL_DESCRIPTOR_FAMILIES: &[ManualDescriptorFamily] = &[
@@ -170,6 +174,11 @@ pub fn trade_types_module() -> Result<DtoTypesModule, String> {
)
}
+pub fn types_types_module() -> Result<DtoTypesModule, String> {
+ let root_set = package_root_set("types").ok_or_else(|| "missing types DTO roots".to_owned())?;
+ render_registry_types(&root_set.registry(), &DtoRegistryRenderOptions::default())
+}
+
fn core_roots() -> Vec<RootDescriptor> {
radroots_core::dto::dto_roots().into_iter().collect()
}
@@ -188,6 +197,10 @@ fn trade_roots() -> Vec<RootDescriptor> {
radroots_trade_bindings::dto_roots()
}
+fn types_roots() -> Vec<RootDescriptor> {
+ radroots_types_bindings::dto_roots()
+}
+
fn core_import_options(
registry: &Registry,
mut options: DtoRegistryRenderOptions,
@@ -484,6 +497,7 @@ mod tests {
assert!(package_root_set("events").is_some());
assert!(package_root_set("events_indexed").is_some());
assert!(package_root_set("trade").is_some());
+ assert!(package_root_set("types").is_some());
}
#[test]
diff --git a/tools/xtask/src/output.rs b/tools/xtask/src/output.rs
@@ -130,7 +130,7 @@ pub fn package_outputs() -> Result<Vec<PackageOutput>, String> {
},
PackageOutput {
spec: spec_by_key("types"),
- types_ts: Some(TsSource::Module(radroots_types_bindings::types_module())),
+ types_ts: Some(TsSource::DtoRegistry(dto_roots::types_types_module()?)),
types_imports_ts: None,
constants_ts: None,
kinds_ts: None,