sdk

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

commit f22049395dee2578a9861ea517411eb250344ddd
parent 6fc0865cd72e40f9b1f7f0e838ac512f8d459a2c
Author: triesap <tyson@radroots.org>
Date:   Thu, 18 Jun 2026 13:45:31 -0700

sdk: add source boundary guard

- guard SDK sources against app and CLI concepts
- check SDK manifest for downstream crate dependencies
- keep app integration ownership outside SDK runtime code
- verify SDK closeout matrix and source-boundary tests

Diffstat:
Acrates/sdk/tests/source_boundary.rs | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 109 insertions(+), 0 deletions(-)

diff --git a/crates/sdk/tests/source_boundary.rs b/crates/sdk/tests/source_boundary.rs @@ -0,0 +1,109 @@ +use std::{ + fs, + path::{Path, PathBuf}, +}; + +struct ForbiddenSdkConcept { + pattern: &'static str, + reason: &'static str, +} + +const FORBIDDEN_SDK_SOURCE_CONCEPTS: &[ForbiddenSdkConcept] = &[ + ForbiddenSdkConcept { + pattern: "radroots_app", + reason: "SDK source must not depend on the app crate family", + }, + ForbiddenSdkConcept { + pattern: "radroots_cli", + reason: "SDK source must not depend on the CLI crate family", + }, + ForbiddenSdkConcept { + pattern: "RadrootsApp", + reason: "SDK source must not import app product concepts", + }, + ForbiddenSdkConcept { + pattern: "RadrootsCli", + reason: "SDK source must not import CLI product concepts", + }, + ForbiddenSdkConcept { + pattern: "AppSdk", + reason: "app SDK adapters belong outside the SDK crate", + }, + ForbiddenSdkConcept { + pattern: "DesktopApp", + reason: "desktop app runtime concepts belong outside the SDK crate", + }, + ForbiddenSdkConcept { + pattern: "domains/radroots/apps", + reason: "SDK source must remain standalone and path-agnostic", + }, +]; + +#[test] +fn sdk_sources_do_not_import_app_or_cli_concepts() { + for path in rust_source_files(Path::new(env!("CARGO_MANIFEST_DIR")).join("src").as_path()) { + let source = read_source(path.as_path()); + for concept in FORBIDDEN_SDK_SOURCE_CONCEPTS { + assert!( + !source.contains(concept.pattern), + "{} contains forbidden SDK source concept `{}`: {}", + path.display(), + concept.pattern, + concept.reason + ); + } + } +} + +#[test] +fn sdk_manifest_does_not_depend_on_app_or_cli_crates() { + let manifest_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("Cargo.toml"); + let manifest = read_source(manifest_path.as_path()); + + for crate_name in ["radroots_app", "radroots_cli"] { + assert!( + !manifest.contains(crate_name), + "SDK manifest contains forbidden downstream crate dependency `{crate_name}`" + ); + } +} + +fn read_source(path: &Path) -> String { + fs::read_to_string(path) + .unwrap_or_else(|error| panic!("failed to read source {}: {error}", path.display())) +} + +fn rust_source_files(root: &Path) -> Vec<PathBuf> { + let mut paths = Vec::new(); + collect_rust_source_files(root, &mut paths); + paths.sort(); + paths +} + +fn collect_rust_source_files(root: &Path, paths: &mut Vec<PathBuf>) { + let entries = fs::read_dir(root).unwrap_or_else(|error| { + panic!( + "failed to read source directory {}: {error}", + root.display() + ) + }); + + for entry in entries { + let entry = entry.unwrap_or_else(|error| { + panic!( + "failed to inspect source directory {}: {error}", + root.display() + ) + }); + let path = entry.path(); + + if path.is_dir() { + collect_rust_source_files(path.as_path(), paths); + continue; + } + + if path.extension().and_then(|extension| extension.to_str()) == Some("rs") { + paths.push(path); + } + } +}