commit 5fdc9b9ab675cd96077dbee9e383ae786035afdf
parent 3802788108a3823ab133074031d499cb415e799e
Author: triesap <tyson@radroots.org>
Date: Wed, 24 Jun 2026 09:08:33 +0000
sdk: remove binding model crate
Delete crates/binding_model and remove it from the workspace, lockfile, and xtask dependencies.
Drop the final TsSource::Module path and keep output tests on raw and DTO-registry sources.
Remove binding_model from coverage exclusions so the obsolete crate is no longer part of SDK policy.
Validate with cargo fmt/check/test, xtask generate/check, pnpm -r build/typecheck, and a zero-hit forbidden-reference search.
Diffstat:
7 files changed, 6 insertions(+), 514 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -2070,10 +2070,6 @@ dependencies = [
]
[[package]]
-name = "radroots_sdk_binding_model"
-version = "0.1.0"
-
-[[package]]
name = "radroots_sdk_sql_wasm_runtime"
version = "0.1.0-alpha.2"
dependencies = [
@@ -2095,7 +2091,6 @@ dependencies = [
"radroots_events_indexed",
"radroots_identity_bindings",
"radroots_replica_db_schema_bindings",
- "radroots_sdk_binding_model",
"radroots_trade_bindings",
"radroots_types_bindings",
"serde",
diff --git a/Cargo.toml b/Cargo.toml
@@ -1,6 +1,5 @@
[workspace]
members = [
- "crates/binding_model",
"crates/core_bindings",
"crates/events_codec_wasm",
"crates/events_bindings",
diff --git a/contracts/coverage.toml b/contracts/coverage.toml
@@ -10,7 +10,7 @@ wasm_target = "wasm32-unknown-unknown"
[report]
output = "target/sdk-coverage/summary.json"
-ignore_filename_regex = "(/target/|/\\.cargo/registry/|/Cellar/rust/|/crates/.+_bindings/|/crates/binding_model/|/crates/replica_db_wasm/src/wasm_impl.rs|/tools/xtask/src/(check|contracts|coverage|fs|generate|main|output|package_matrix|wasm)\\.rs)"
+ignore_filename_regex = "(/target/|/\\.cargo/registry/|/Cellar/rust/|/crates/.+_bindings/|/crates/replica_db_wasm/src/wasm_impl.rs|/tools/xtask/src/(check|contracts|coverage|fs|generate|main|output|package_matrix|wasm)\\.rs)"
[scopes.radroots_sdk]
paths = ["crates/sdk/src/**"]
@@ -46,7 +46,7 @@ paths = ["packages/*/src/generated/**", "packages/*/dist/**"]
reason = "generated package output is checked through cargo xtask check, cargo xtask generate ts, cargo xtask generate wasm, and pnpm typecheck"
[exclusions.generated_binding_crates]
-paths = ["crates/*_bindings/**", "crates/binding_model/**"]
+paths = ["crates/*_bindings/**"]
reason = "binding crates are generator-owned source facades with behavior covered by xtask generator tests"
[exclusions.wasm_glue_bootstrap]
diff --git a/crates/binding_model/Cargo.toml b/crates/binding_model/Cargo.toml
@@ -1,11 +0,0 @@
-[package]
-name = "radroots_sdk_binding_model"
-version.workspace = true
-edition.workspace = true
-rust-version.workspace = true
-license.workspace = true
-repository.workspace = true
-homepage.workspace = true
-publish = false
-
-[dependencies]
diff --git a/crates/binding_model/src/lib.rs b/crates/binding_model/src/lib.rs
@@ -1,478 +0,0 @@
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct TsModule {
- declarations: Vec<TsDeclaration>,
-}
-
-impl TsModule {
- pub fn new(declarations: Vec<TsDeclaration>) -> Self {
- Self { declarations }
- }
-
- pub fn render(&self) -> String {
- let mut rendered = String::new();
- for (index, declaration) in self.declarations.iter().enumerate() {
- if index > 0 {
- rendered.push_str(render_separator(&self.declarations[index - 1], declaration));
- }
- rendered.push_str(&declaration.render());
- }
- rendered
- }
-}
-
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub enum TsDeclaration {
- TypeAlias(TsTypeAlias),
- Const(TsConst),
- ImportType(TsImportType),
-}
-
-impl TsDeclaration {
- fn render(&self) -> String {
- match self {
- Self::TypeAlias(alias) => alias.render(),
- Self::Const(constant) => constant.render(),
- Self::ImportType(import) => import.render(),
- }
- }
-}
-
-fn render_separator(previous: &TsDeclaration, current: &TsDeclaration) -> &'static str {
- match (previous, current) {
- (TsDeclaration::Const(_), TsDeclaration::Const(_)) => "\n",
- _ => "\n\n",
- }
-}
-
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct TsTypeAlias {
- name: String,
- params: Vec<String>,
- value: TsType,
-}
-
-impl TsTypeAlias {
- fn render(&self) -> String {
- let params = if self.params.is_empty() {
- String::new()
- } else {
- format!("<{}>", self.params.join(", "))
- };
- format!(
- "export type {}{} = {};",
- self.name,
- params,
- self.value.render()
- )
- }
-}
-
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct TsConst {
- name: String,
- annotation: Option<TsType>,
- value: TsValue,
-}
-
-impl TsConst {
- fn render(&self) -> String {
- let annotation = self
- .annotation
- .as_ref()
- .map(|value| format!(": {}", value.render()))
- .unwrap_or_default();
- format!(
- "export const {}{} = {};",
- self.name,
- annotation,
- self.value.render()
- )
- }
-}
-
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct TsImportType {
- names: Vec<String>,
- from: String,
-}
-
-impl TsImportType {
- fn render(&self) -> String {
- if self.names.len() == 1 {
- return format!(
- "import type {{ {} }} from {};",
- self.names[0],
- quote_string(&self.from)
- );
- }
- let names = self
- .names
- .iter()
- .map(|name| format!(" {name},"))
- .collect::<Vec<_>>()
- .join("\n");
- format!(
- "import type {{\n{}\n}} from {};",
- names,
- quote_string(&self.from)
- )
- }
-}
-
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub enum TsType {
- Primitive(TsPrimitive),
- Reference { name: String, args: Vec<TsType> },
- Array(Box<TsType>),
- Tuple { readonly: bool, items: Vec<TsType> },
- Object(Vec<TsField>),
- Union(Vec<TsType>),
- StringLiteral(String),
- NumberLiteral(i64),
-}
-
-impl TsType {
- pub fn render(&self) -> String {
- match self {
- Self::Primitive(value) => value.render().to_owned(),
- Self::Reference { name, args } => {
- if args.is_empty() {
- name.clone()
- } else {
- format!(
- "{}<{}>",
- name,
- args.iter()
- .map(TsType::render)
- .collect::<Vec<_>>()
- .join(", ")
- )
- }
- }
- Self::Array(item) => format!("Array<{}>", item.render()),
- Self::Tuple { readonly, items } => {
- let prefix = if *readonly { "readonly " } else { "" };
- format!(
- "{}[{}]",
- prefix,
- items
- .iter()
- .map(TsType::render)
- .collect::<Vec<_>>()
- .join(", ")
- )
- }
- Self::Object(fields) => {
- if fields.is_empty() {
- return "{}".to_owned();
- }
- format!(
- "{{ {}, }}",
- fields
- .iter()
- .map(TsField::render)
- .collect::<Vec<_>>()
- .join(", ")
- )
- }
- Self::Union(items) => {
- if items.is_empty() {
- return "never".to_owned();
- }
- items
- .iter()
- .map(TsType::render)
- .collect::<Vec<_>>()
- .join(" | ")
- }
- Self::StringLiteral(value) => quote_string(value),
- Self::NumberLiteral(value) => value.to_string(),
- }
- }
-}
-
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub enum TsPrimitive {
- String,
- Number,
- Boolean,
- BigInt,
- Null,
-}
-
-impl TsPrimitive {
- fn render(&self) -> &'static str {
- match self {
- Self::String => "string",
- Self::Number => "number",
- Self::Boolean => "boolean",
- Self::BigInt => "bigint",
- Self::Null => "null",
- }
- }
-}
-
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct TsField {
- name: String,
- optional: bool,
- value: TsType,
-}
-
-impl TsField {
- fn render(&self) -> String {
- let optional = if self.optional { "?" } else { "" };
- format!(
- "{}{}: {}",
- render_property_name(&self.name),
- optional,
- self.value.render()
- )
- }
-}
-
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub enum TsValue {
- Number(i64),
- String(String),
- Boolean(bool),
- Array(Vec<TsValue>),
-}
-
-impl TsValue {
- fn render(&self) -> String {
- match self {
- Self::Number(value) => value.to_string(),
- Self::String(value) => quote_string(value),
- Self::Boolean(value) => value.to_string(),
- Self::Array(values) => format!(
- "[{}]",
- values
- .iter()
- .map(TsValue::render)
- .collect::<Vec<_>>()
- .join(", ")
- ),
- }
- }
-}
-
-pub fn module(declarations: Vec<TsDeclaration>) -> TsModule {
- TsModule::new(declarations)
-}
-
-pub fn import_type(names: &[&str], from: &str) -> TsDeclaration {
- TsDeclaration::ImportType(TsImportType {
- names: names.iter().map(|name| (*name).to_owned()).collect(),
- from: from.to_owned(),
- })
-}
-
-pub fn type_alias(name: &str, value: TsType) -> TsDeclaration {
- type_alias_params(name, &[], value)
-}
-
-pub fn type_alias_params(name: &str, params: &[&str], value: TsType) -> TsDeclaration {
- TsDeclaration::TypeAlias(TsTypeAlias {
- name: name.to_owned(),
- params: params.iter().map(|param| (*param).to_owned()).collect(),
- value,
- })
-}
-
-pub fn const_number(name: &str, value: i64) -> TsDeclaration {
- const_decl(name, None, TsValue::Number(value))
-}
-
-pub fn const_string_array(name: &str, annotation: TsType, values: &[&str]) -> TsDeclaration {
- const_decl(
- name,
- Some(annotation),
- TsValue::Array(
- values
- .iter()
- .map(|value| TsValue::String((*value).to_owned()))
- .collect(),
- ),
- )
-}
-
-pub fn const_decl(name: &str, annotation: Option<TsType>, value: TsValue) -> TsDeclaration {
- TsDeclaration::Const(TsConst {
- name: name.to_owned(),
- annotation,
- value,
- })
-}
-
-pub fn string() -> TsType {
- TsType::Primitive(TsPrimitive::String)
-}
-
-pub fn number() -> TsType {
- TsType::Primitive(TsPrimitive::Number)
-}
-
-pub fn boolean() -> TsType {
- TsType::Primitive(TsPrimitive::Boolean)
-}
-
-pub fn bigint() -> TsType {
- TsType::Primitive(TsPrimitive::BigInt)
-}
-
-pub fn null() -> TsType {
- TsType::Primitive(TsPrimitive::Null)
-}
-
-pub fn reference(name: &str) -> TsType {
- TsType::Reference {
- name: name.to_owned(),
- args: Vec::new(),
- }
-}
-
-pub fn generic(name: &str, args: Vec<TsType>) -> TsType {
- TsType::Reference {
- name: name.to_owned(),
- args,
- }
-}
-
-pub fn array(item: TsType) -> TsType {
- TsType::Array(Box::new(item))
-}
-
-pub fn tuple(items: Vec<TsType>) -> TsType {
- TsType::Tuple {
- readonly: false,
- items,
- }
-}
-
-pub fn readonly_tuple(items: Vec<TsType>) -> TsType {
- TsType::Tuple {
- readonly: true,
- items,
- }
-}
-
-pub fn object(fields: Vec<TsField>) -> TsType {
- TsType::Object(fields)
-}
-
-pub fn union(items: Vec<TsType>) -> TsType {
- let mut flattened = Vec::new();
- for item in items {
- match item {
- TsType::Union(items) => flattened.extend(items),
- item => flattened.push(item),
- }
- }
- TsType::Union(flattened)
-}
-
-pub fn nullable(item: TsType) -> TsType {
- union(vec![item, null()])
-}
-
-pub fn string_literal(value: &str) -> TsType {
- TsType::StringLiteral(value.to_owned())
-}
-
-pub fn number_literal(value: i64) -> TsType {
- TsType::NumberLiteral(value)
-}
-
-pub fn field(name: &str, value: TsType) -> TsField {
- TsField {
- name: name.to_owned(),
- optional: false,
- value,
- }
-}
-
-pub fn optional_field(name: &str, value: TsType) -> TsField {
- TsField {
- name: name.to_owned(),
- optional: true,
- value,
- }
-}
-
-fn render_property_name(value: &str) -> String {
- if is_identifier(value) {
- value.to_owned()
- } else {
- quote_string(value)
- }
-}
-
-fn is_identifier(value: &str) -> bool {
- let mut chars = value.chars();
- match chars.next() {
- Some(first) if first == '_' || first == '$' || first.is_ascii_alphabetic() => {}
- _ => return false,
- }
- chars.all(|ch| ch == '_' || ch == '$' || ch.is_ascii_alphanumeric())
-}
-
-fn quote_string(value: &str) -> String {
- let mut escaped = String::with_capacity(value.len() + 2);
- escaped.push('"');
- for ch in value.chars() {
- match ch {
- '\\' => escaped.push_str("\\\\"),
- '"' => escaped.push_str("\\\""),
- '\n' => escaped.push_str("\\n"),
- '\r' => escaped.push_str("\\r"),
- '\t' => escaped.push_str("\\t"),
- ch => escaped.push(ch),
- }
- }
- escaped.push('"');
- escaped
-}
-
-#[cfg(test)]
-mod tests {
- use super::{
- array, const_number, field, import_type, module, nullable, number, object, reference,
- string, string_literal, type_alias, type_alias_params, union,
- };
-
- #[test]
- fn renders_type_aliases() {
- let module = module(vec![
- type_alias("Name", string()),
- type_alias_params(
- "Result",
- &["T"],
- object(vec![field("result", reference("T"))]),
- ),
- ]);
- assert_eq!(
- module.render(),
- "export type Name = string;\n\nexport type Result<T> = { result: T, };"
- );
- }
-
- #[test]
- fn renders_imports_constants_and_unions() {
- let module = module(vec![
- import_type(&["A", "B"], "@radroots/example"),
- type_alias(
- "Status",
- union(vec![string_literal("ready"), nullable(array(number()))]),
- ),
- const_number("KIND_READY", 1),
- ]);
- assert!(module.render().contains("import type {\n A,\n B,\n}"));
- assert!(
- module
- .render()
- .contains("export type Status = \"ready\" | Array<number> | null;")
- );
- assert!(module.render().contains("export const KIND_READY = 1;"));
- }
-}
diff --git a/tools/xtask/Cargo.toml b/tools/xtask/Cargo.toml
@@ -14,7 +14,6 @@ path = "src/main.rs"
[dependencies]
dto_bindgen_core = { workspace = true }
-radroots_sdk_binding_model = { path = "../../crates/binding_model" }
radroots_core = { workspace = true, features = ["dto-bindgen"] }
radroots_events = { workspace = true, features = ["dto-bindgen"] }
radroots_events_bindings = { path = "../../crates/events_bindings" }
diff --git a/tools/xtask/src/output.rs b/tools/xtask/src/output.rs
@@ -6,7 +6,6 @@ use crate::{
package_matrix::{PackageSpec, package_specs},
ts::{generated_constants_file, generated_header, generated_kinds_file, generated_types_file},
};
-use radroots_sdk_binding_model::TsModule;
pub struct PackageOutput {
pub spec: PackageSpec,
@@ -21,10 +20,8 @@ pub struct GeneratedFile {
pub contents: String,
}
-#[allow(dead_code)]
pub enum TsSource {
DtoRegistry(DtoTypesModule),
- Module(TsModule),
Raw(String),
}
@@ -32,7 +29,6 @@ impl TsSource {
fn render(&self) -> String {
match self {
Self::DtoRegistry(module) => module.body_ts().to_owned(),
- Self::Module(module) => module.render(),
Self::Raw(body) => body.clone(),
}
}
@@ -40,7 +36,6 @@ impl TsSource {
fn imports(&self) -> Option<&str> {
match self {
Self::DtoRegistry(module) => module.imports_ts(),
- Self::Module(_) => None,
Self::Raw(_) => None,
}
}
@@ -203,7 +198,6 @@ fn render_index(output: &PackageOutput) -> String {
mod tests {
use super::{PackageOutput, TsSource, package_outputs, render_ts};
use crate::{dto_render::DtoTypesModule, package_matrix::package_specs};
- use radroots_sdk_binding_model::{module, string, type_alias};
const TRADE_BINDINGS_TYPES_TS: &str =
include_str!("../../../packages/trade-bindings/src/generated/types.ts");
@@ -212,10 +206,7 @@ mod tests {
#[test]
fn renders_sdk_header() {
- let output = render_ts(
- &TsSource::Module(module(vec![type_alias("A", string())])),
- None,
- );
+ let output = render_ts(&TsSource::Raw("export type A = string;".to_owned()), None);
assert!(output.starts_with("// @generated by cargo xtask generate ts"));
assert!(output.contains("export type A = string;"));
}
@@ -223,7 +214,7 @@ mod tests {
#[test]
fn renders_import_prelude_after_header() {
let output = render_ts(
- &TsSource::Module(module(vec![type_alias("A", string())])),
+ &TsSource::Raw("export type A = string;".to_owned()),
Some("import type { B } from \"b\";\n\n"),
);
assert!(output.starts_with(
@@ -233,11 +224,8 @@ mod tests {
}
#[test]
- fn renders_model_sources() {
- let output = render_ts(
- &TsSource::Module(module(vec![type_alias("A", string())])),
- None,
- );
+ fn renders_raw_sources() {
+ let output = render_ts(&TsSource::Raw("export type A = string;".to_owned()), None);
assert_eq!(
output,
"// @generated by cargo xtask generate ts\n// Do not edit by hand.\nexport type A = string;\n"