From fb41fd9b71fa56d5f4f689aa8316b1b08b7e26f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C3=96zkan?= Date: Sun, 19 Jul 2026 22:39:55 +0300 Subject: [PATCH] libs/kaptan-spec: remove specification validator --- libs/kaptan-spec/Cargo.toml | 19 --- libs/kaptan-spec/src/lib.rs | 128 ------------------ libs/kaptan-spec/src/lib.spec.md | 75 ---------- libs/kaptan-spec/tests/spec_macro.rs | 14 -- .../tests/ui/fail_empty_explanation.rs | 5 - .../tests/ui/fail_empty_explanation.stderr | 7 - .../tests/ui/fail_missing_argument.rs | 5 - .../tests/ui/fail_missing_argument.stderr | 7 - .../tests/ui/fail_non_string_argument.rs | 5 - .../tests/ui/fail_non_string_argument.stderr | 20 --- libs/kaptan-spec/tests/ui/no_spec.rs | 5 - 11 files changed, 290 deletions(-) delete mode 100644 libs/kaptan-spec/Cargo.toml delete mode 100644 libs/kaptan-spec/src/lib.rs delete mode 100644 libs/kaptan-spec/src/lib.spec.md delete mode 100644 libs/kaptan-spec/tests/spec_macro.rs delete mode 100644 libs/kaptan-spec/tests/ui/fail_empty_explanation.rs delete mode 100644 libs/kaptan-spec/tests/ui/fail_empty_explanation.stderr delete mode 100644 libs/kaptan-spec/tests/ui/fail_missing_argument.rs delete mode 100644 libs/kaptan-spec/tests/ui/fail_missing_argument.stderr delete mode 100644 libs/kaptan-spec/tests/ui/fail_non_string_argument.rs delete mode 100644 libs/kaptan-spec/tests/ui/fail_non_string_argument.stderr delete mode 100644 libs/kaptan-spec/tests/ui/no_spec.rs diff --git a/libs/kaptan-spec/Cargo.toml b/libs/kaptan-spec/Cargo.toml deleted file mode 100644 index 0fc59cb..0000000 --- a/libs/kaptan-spec/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "kaptan-spec" -edition.workspace = true -license.workspace = true -repository.workspace = true -rust-version.workspace = true -version.workspace = true - -[lints] -workspace = true - -[features] -std = ["dep:syn"] - -[dependencies] -syn = { version = "2", features = ["full"], optional = true } - -[dev-dependencies] -trybuild = "1.0" \ No newline at end of file diff --git a/libs/kaptan-spec/src/lib.rs b/libs/kaptan-spec/src/lib.rs deleted file mode 100644 index 7519a58..0000000 --- a/libs/kaptan-spec/src/lib.rs +++ /dev/null @@ -1,128 +0,0 @@ -//! Kaptan formal-specification infrastructure. - -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(feature = "std")] -use std::{env, fs, path::Path}; - -#[cfg(feature = "std")] -use syn::Item; - -/// Returns whether an exception explanation contains a non-whitespace byte. -#[doc(hidden)] -pub const fn has_non_whitespace(value: &str) -> bool { - let bytes = value.as_bytes(); - let mut index = 0; - - while index < bytes.len() { - if !bytes[index].is_ascii_whitespace() { - return true; - } - - index += 1; - } - - false -} - -/// Declares that the containing module intentionally has no sidecar spec. -#[macro_export] -macro_rules! no_spec { - () => { - compile_error!("missing no-spec explanation; use no_spec!(\"reason\");"); - }; - ($reason:literal) => { - const _: &str = $reason; - const _: () = assert!( - $crate::has_non_whitespace($reason), - "no-spec explanation must not be empty; use no_spec!(\"reason\");" - ); - }; - ($($invalid:tt)+) => { - compile_error!("invalid no-spec invocation; use no_spec!(\"reason\");"); - }; -} - -/// Validates the Rust modules in the package identified by Cargo's build-script -/// environment. -/// -/// Every file-backed module under `src/` must have exactly one of a same-name -/// `.spec.md` sidecar or a top-level `no_spec!("reason");` declaration. -#[cfg(feature = "std")] -pub fn validate_package() { - let source_directory = - Path::new(&env::var("CARGO_MANIFEST_DIR").expect("manifest directory")).join("src"); - let mut violations = Vec::new(); - - check_directory(&source_directory, &mut violations); - - if !violations.is_empty() { - panic!( - "Kaptan module specification check failed:\n{}", - violations.join("\n") - ); - } -} - -#[cfg(feature = "std")] -fn check_directory(directory: &Path, violations: &mut Vec) { - let entries = fs::read_dir(directory).unwrap_or_else(|error| { - panic!( - "could not read module directory {}: {error}", - directory.display() - ) - }); - - for entry in entries { - let path = entry - .unwrap_or_else(|error| panic!("could not read module entry: {error}")) - .path(); - - if path.is_dir() { - check_directory(&path, violations); - } else if path.extension().is_some_and(|extension| extension == "rs") { - check_module_file(&path, violations); - } - } -} - -#[cfg(feature = "std")] -fn check_module_file(module_path: &Path, violations: &mut Vec) { - println!("cargo:rerun-if-changed={}", module_path.display()); - - let source = fs::read_to_string(module_path) - .unwrap_or_else(|error| panic!("could not read module {}: {error}", module_path.display())); - let module = syn::parse_file(&source).unwrap_or_else(|error| { - panic!("could not parse module {}: {error}", module_path.display()) - }); - let sidecar_path = module_path.with_extension("spec.md"); - let has_sidecar = sidecar_path.is_file(); - let has_no_spec = module.items.iter().any( - |item| matches!(item, Item::Macro(item_macro) if is_no_spec_macro(&item_macro.mac.path)), - ); - - if has_sidecar { - println!("cargo:rerun-if-changed={}", sidecar_path.display()); - } - - match (has_sidecar, has_no_spec) { - (false, false) => violations.push(format!( - "- {} is missing {}; add the sidecar or no_spec!(\"reason\").", - module_path.display(), - sidecar_path.display() - )), - (true, true) => violations.push(format!( - "- {} has both {} and no_spec!(...); keep exactly one declaration.", - module_path.display(), - sidecar_path.display() - )), - _ => {} - } -} - -#[cfg(feature = "std")] -fn is_no_spec_macro(path: &syn::Path) -> bool { - path.segments - .last() - .is_some_and(|segment| segment.ident == "no_spec") -} diff --git a/libs/kaptan-spec/src/lib.spec.md b/libs/kaptan-spec/src/lib.spec.md deleted file mode 100644 index 9ff34c2..0000000 --- a/libs/kaptan-spec/src/lib.spec.md +++ /dev/null @@ -1,75 +0,0 @@ -# --- version: 0.1.0 --- - ---- -title: Specification validation library -status: in-progress -owners: - - Onur Özkan ---- - -## Scope - -This module defines Kaptan's reusable package build-time validation of formal-specification sidecars and the no_std `no_spec!` exception macro. - -Formal Kaptan specifications live next to the file-backed module they govern. For example, `control_loop.rs` is governed by `control_loop.spec.md` in the same directory. - -## Sidecar Requirements - -Every file-backed Rust module under a validated package's `src/` tree must have exactly one same-name `.spec.md` sidecar or one top-level `no_spec!("reason");` declaration. Missing and duplicate declarations fail the package build. - -A package build script invokes `kaptan_spec::validate_package()` with the `std` feature enabled. The validator emits Cargo change-tracking directives for every checked source module and existing sidecar. - -`kaptan-spec` is the bootstrap exception because it cannot use itself as a build dependency. Every other Kaptan package must invoke the validator from its build script. - -## No-Spec Exceptions - -`no_spec!("reason");` is only for a file-backed module that intentionally has no standalone formal specification, such as a crate root that only declares child modules. It applies at module scope, not to types, functions, or individual items. - -The exception requires one non-empty, non-whitespace string literal explanation. It expands to compile-time constants and has no runtime cost. A module must not contain both a sidecar and `no_spec!(...)`. - -## Specification Record Format - -Each sidecar is an append-only record for one governed module or package. An entry starts with this exact version heading: - -```markdown -# --- version: 0.1.0 --- -``` - -The heading is followed by YAML frontmatter containing these required keys: - -- `title` -- `status` -- `owners` - -Allowed statuses are: - -- `in-progress` -- `implemented` -- `deprecated` - -Each owner entry uses a full name and email address: - -```text -- Full Name -``` - -## Traceability and Revisions - -The sidecar path identifies the governed module or package. Individual revisions are identified by their version heading. New revisions append a complete new entry to the stable sidecar file; they do not create a new filename. - -Within a record entry, `#` is reserved for version headings. Content headings start at `##` or below. - -## Entry Template - -```markdown -# --- version: 0.1.0 --- - ---- -title: Replace with title -status: deprecated -owners: - - Full Name ---- - -## TODO -``` \ No newline at end of file diff --git a/libs/kaptan-spec/tests/spec_macro.rs b/libs/kaptan-spec/tests/spec_macro.rs deleted file mode 100644 index f36d91a..0000000 --- a/libs/kaptan-spec/tests/spec_macro.rs +++ /dev/null @@ -1,14 +0,0 @@ -#[test] -fn accepts_no_spec_reason() { - let tests = trybuild::TestCases::new(); - tests.pass("tests/ui/no_spec.rs"); -} - -#[test] -fn rejects_invalid_no_spec_invocations() { - let tests = trybuild::TestCases::new(); - - tests.compile_fail("tests/ui/fail_missing_argument.rs"); - tests.compile_fail("tests/ui/fail_non_string_argument.rs"); - tests.compile_fail("tests/ui/fail_empty_explanation.rs"); -} diff --git a/libs/kaptan-spec/tests/ui/fail_empty_explanation.rs b/libs/kaptan-spec/tests/ui/fail_empty_explanation.rs deleted file mode 100644 index 9de6eb4..0000000 --- a/libs/kaptan-spec/tests/ui/fail_empty_explanation.rs +++ /dev/null @@ -1,5 +0,0 @@ -use kaptan_spec::no_spec; - -no_spec!(" "); - -fn main() {} \ No newline at end of file diff --git a/libs/kaptan-spec/tests/ui/fail_empty_explanation.stderr b/libs/kaptan-spec/tests/ui/fail_empty_explanation.stderr deleted file mode 100644 index e68dcb8..0000000 --- a/libs/kaptan-spec/tests/ui/fail_empty_explanation.stderr +++ /dev/null @@ -1,7 +0,0 @@ -error[E0080]: evaluation panicked: no-spec explanation must not be empty; use no_spec!("reason"); - --> tests/ui/fail_empty_explanation.rs:3:1 - | -3 | no_spec!(" "); - | ^^^^^^^^^^^^^^^ evaluation of `_` failed here - | - = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `no_spec` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/libs/kaptan-spec/tests/ui/fail_missing_argument.rs b/libs/kaptan-spec/tests/ui/fail_missing_argument.rs deleted file mode 100644 index dbc02c8..0000000 --- a/libs/kaptan-spec/tests/ui/fail_missing_argument.rs +++ /dev/null @@ -1,5 +0,0 @@ -use kaptan_spec::no_spec; - -no_spec!(); - -fn main() {} \ No newline at end of file diff --git a/libs/kaptan-spec/tests/ui/fail_missing_argument.stderr b/libs/kaptan-spec/tests/ui/fail_missing_argument.stderr deleted file mode 100644 index 8734b9b..0000000 --- a/libs/kaptan-spec/tests/ui/fail_missing_argument.stderr +++ /dev/null @@ -1,7 +0,0 @@ -error: missing no-spec explanation; use no_spec!("reason"); - --> tests/ui/fail_missing_argument.rs:3:1 - | -3 | no_spec!(); - | ^^^^^^^^^^ - | - = note: this error originates in the macro `no_spec` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/libs/kaptan-spec/tests/ui/fail_non_string_argument.rs b/libs/kaptan-spec/tests/ui/fail_non_string_argument.rs deleted file mode 100644 index ecce319..0000000 --- a/libs/kaptan-spec/tests/ui/fail_non_string_argument.rs +++ /dev/null @@ -1,5 +0,0 @@ -use kaptan_spec::no_spec; - -no_spec!(42); - -fn main() {} \ No newline at end of file diff --git a/libs/kaptan-spec/tests/ui/fail_non_string_argument.stderr b/libs/kaptan-spec/tests/ui/fail_non_string_argument.stderr deleted file mode 100644 index 0fdac3a..0000000 --- a/libs/kaptan-spec/tests/ui/fail_non_string_argument.stderr +++ /dev/null @@ -1,20 +0,0 @@ -error[E0308]: mismatched types - --> tests/ui/fail_non_string_argument.rs:3:10 - | -3 | no_spec!(42); - | ^^ expected `&str`, found integer - -error[E0308]: mismatched types - --> tests/ui/fail_non_string_argument.rs:3:10 - | -3 | no_spec!(42); - | ---------^^- - | | | - | | expected `&str`, found integer - | arguments to this function are incorrect - | -note: function defined here - --> src/lib.rs - | - | pub const fn has_non_whitespace(value: &str) -> bool { - | ^^^^^^^^^^^^^^^^^^ diff --git a/libs/kaptan-spec/tests/ui/no_spec.rs b/libs/kaptan-spec/tests/ui/no_spec.rs deleted file mode 100644 index 7edfe0f..0000000 --- a/libs/kaptan-spec/tests/ui/no_spec.rs +++ /dev/null @@ -1,5 +0,0 @@ -use kaptan_spec::no_spec; - -no_spec!("This fixture has no governed runtime behavior."); - -fn main() {} \ No newline at end of file