sdk

Radroots SDK and bindings
git clone https://radroots.dev/git/sdk.git
Log | Files | Refs | README

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:
MCargo.lock | 5-----
MCargo.toml | 1-
Mcontracts/coverage.toml | 4++--
Dcrates/binding_model/Cargo.toml | 11-----------
Dcrates/binding_model/src/lib.rs | 478-------------------------------------------------------------------------------
Mtools/xtask/Cargo.toml | 1-
Mtools/xtask/src/output.rs | 20++++----------------
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"