initialize spec macro and standards
Signed-off-by: Onur Özkan <work@onurozkan.dev>
This commit is contained in:
1 parent
bac05a4562
commit
ceba320a96
11 files changed
+471
-1
No files matched your search
Generated
+238
@@ -2,6 +2,244 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "kaptan-runtime"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "kaptan-spec"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"trybuild",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.119"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "target-triple"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "591ef38edfb78ca4771ee32cf494cb8771944bee237a9b91fc9c1424ac4b777b"
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "1.1.3+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53c96ecdfa941c8fc4fcaed14f99ada8ebed502eef533015095a07e3301d4c3c"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde_core",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_parser",
|
||||
"toml_writer",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "1.1.1+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.1.2+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
|
||||
dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_writer"
|
||||
version = "1.1.2+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
|
||||
|
||||
[[package]]
|
||||
name = "trybuild"
|
||||
version = "1.0.118"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06649c6f63d86604ba0c8950d5a1829fc9a17afd70fc6629f481d75b6a624c78"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"target-triple",
|
||||
"termcolor",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81"
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
|
||||
@@ -1,5 +1,6 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"kaptan-spec",
|
||||
"kaptan-runtime",
|
||||
]
|
||||
resolver = "2"
|
||||
|
||||
@@ -1,2 +1,42 @@
|
||||
# kaptan
|
||||
next-gen flight controller operating system
|
||||
|
||||
Kaptan is a modular flight controller platform focused on deterministic bare-metal execution for flight-critical paths and strong verification discipline around the rest of the stack.
|
||||
|
||||
The repository is currently in infrastructure setup. Formal project specifications should be linked directly from the code they govern through compile-time attributes.
|
||||
|
||||
## Current Direction
|
||||
|
||||
- Flight-critical runtime stays bare-metal with no RTOS in the control path.
|
||||
- The first vehicle scope is multirotor.
|
||||
- The first board support lane is Kakute F7 Mini V3.
|
||||
- Non-critical extensibility, companion integration, and user-facing tooling will be added only after core runtime and verification contracts are established.
|
||||
|
||||
## Immediate Priorities
|
||||
|
||||
- Create specification infrastructure without inventing the first formal spec.
|
||||
- Establish crate and board boundaries that fit the first supported hardware lane.
|
||||
- Add deterministic host-side validation before simulation and hardware automation expand.
|
||||
|
||||
## Repository Structure
|
||||
|
||||
- `kaptan-runtime/`: bare-metal runtime crate scaffold.
|
||||
- `tests/sitl/`: host-side simulation and deterministic verification scaffolding.
|
||||
- `tests/hitl/`: hardware-in-the-loop scaffolding.
|
||||
- `scripts/`: repository validation and developer helper scripts.
|
||||
|
||||
## Spec Infrastructure
|
||||
|
||||
Formal specs should be colocated with the code or board package they govern.
|
||||
|
||||
- Implementation-linked specs should use versioned sidecar filenames such as `control_loop.v0.1.0.spec.md` next to the related module or package.
|
||||
- Rust items should link to those sidecar specs through `#[spec("...")]`.
|
||||
- Missing linked spec files must fail compilation.
|
||||
|
||||
Example:
|
||||
|
||||
```rust
|
||||
use kaptan_spec::spec;
|
||||
|
||||
#[spec("control_loop.v0.1.0.spec.md")]
|
||||
pub struct ControlLoop;
|
||||
```
|
||||
@@ -0,0 +1,65 @@
|
||||
# Specifications
|
||||
|
||||
Formal Kaptan specifications should normally live next to the code item they govern.
|
||||
|
||||
Examples:
|
||||
|
||||
- `kaptan-runtime/src/control_loop.rs`
|
||||
- `kaptan-runtime/src/control_loop.v0.1.0.spec.md`
|
||||
- `kaptan-runtime/src/scheduler.rs`
|
||||
- `kaptan-runtime/src/scheduler.v0.1.0.spec.md`
|
||||
|
||||
## Compile-Time Linking
|
||||
|
||||
Code implementations should link to their governing spec through `#[spec("...")]`.
|
||||
|
||||
Example:
|
||||
|
||||
```rust
|
||||
use kaptan_spec::spec;
|
||||
|
||||
#[spec("control_loop.v0.1.0.spec.md")]
|
||||
pub struct ControlLoop;
|
||||
```
|
||||
|
||||
The attribute expands to a compile-time file inclusion guard. If the linked file does not exist, compilation fails.
|
||||
|
||||
## Spec Format
|
||||
|
||||
Formal specs are expected to declare these keys in YAML frontmatter:
|
||||
|
||||
- `title`
|
||||
- `status`
|
||||
- `owners`
|
||||
|
||||
Allowed statuses are:
|
||||
|
||||
- `in-progress`
|
||||
- `implemented`
|
||||
- `deprecated`
|
||||
|
||||
Owner entries must use full name and email address in this format:
|
||||
|
||||
```text
|
||||
- Full Name <name@example.com>
|
||||
```
|
||||
|
||||
The spec path is the identifier for linkage and traceability.
|
||||
|
||||
- The code path plus the `#[spec("...")]` reference identifies the governing spec.
|
||||
- The version is part of the spec filename and therefore part of the spec identity.
|
||||
|
||||
## Template
|
||||
|
||||
Use this starter when creating a new colocated spec:
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: Replace with title
|
||||
status: in-progress
|
||||
owners:
|
||||
- Full Name <name@example.com>
|
||||
---
|
||||
|
||||
# TODO
|
||||
```
|
||||
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "kaptan-spec"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dev-dependencies]
|
||||
trybuild = "1.0"
|
||||
@@ -0,0 +1,70 @@
|
||||
use proc_macro::{Literal, Punct, Spacing, TokenStream, TokenTree};
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn spec(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
match parse_spec_path(attr) {
|
||||
Ok(path) => expand_spec_guard(&path, item),
|
||||
Err(message) => compile_error(&message),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_spec_path(attr: TokenStream) -> Result<String, String> {
|
||||
let tokens: Vec<TokenTree> = attr.into_iter().collect();
|
||||
|
||||
match tokens.as_slice() {
|
||||
[TokenTree::Literal(path)] if is_string_literal(path) => Ok(path.to_string()),
|
||||
[
|
||||
TokenTree::Ident(name),
|
||||
TokenTree::Punct(eq),
|
||||
TokenTree::Literal(path),
|
||||
] if name.to_string() == "path" && is_equals(eq) && is_string_literal(path) => {
|
||||
Ok(path.to_string())
|
||||
}
|
||||
[] => Err("missing spec path; use #[spec(\"file.v0.1.0.spec.md\")]".into()),
|
||||
_ => Err("invalid spec attribute; use #[spec(\"file.v0.1.0.spec.md\")]".into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_string_literal(literal: &Literal) -> bool {
|
||||
let value = literal.to_string();
|
||||
|
||||
value.starts_with('"') || value.starts_with("r\"") || value.starts_with("r#")
|
||||
}
|
||||
|
||||
fn is_equals(punct: &Punct) -> bool {
|
||||
punct.as_char() == '=' && punct.spacing() == Spacing::Alone
|
||||
}
|
||||
|
||||
fn expand_spec_guard(path: &str, item: TokenStream) -> TokenStream {
|
||||
let guard = format!("const _: &str = include_str!({path});");
|
||||
let mut output = TokenStream::new();
|
||||
|
||||
output.extend(
|
||||
guard
|
||||
.parse::<TokenStream>()
|
||||
.expect("spec macro must generate valid Rust tokens"),
|
||||
);
|
||||
output.extend(item);
|
||||
output
|
||||
}
|
||||
|
||||
fn compile_error(message: &str) -> TokenStream {
|
||||
let escaped = escape_string(message);
|
||||
format!("compile_error!(\"{escaped}\");")
|
||||
.parse::<TokenStream>()
|
||||
.expect("spec macro must generate valid compile_error tokens")
|
||||
}
|
||||
|
||||
fn escape_string(value: &str) -> String {
|
||||
let mut escaped = String::with_capacity(value.len());
|
||||
|
||||
for ch in value.chars() {
|
||||
match ch {
|
||||
'\\' => escaped.push_str("\\\\"),
|
||||
'"' => escaped.push_str("\\\""),
|
||||
_ => escaped.push(ch),
|
||||
}
|
||||
}
|
||||
|
||||
escaped
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#[test]
|
||||
fn accepts_existing_sidecar_spec() {
|
||||
let tests = trybuild::TestCases::new();
|
||||
tests.pass("tests/ui/flight_core_contract.rs");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_missing_sidecar_spec() {
|
||||
let tests = trybuild::TestCases::new();
|
||||
tests.compile_fail("tests/ui/fail_missing_sidecar.rs");
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
use kaptan_spec::spec;
|
||||
|
||||
#[spec("fail_missing_sidecar.v0.1.0.spec.md")]
|
||||
struct MissingSpec;
|
||||
|
||||
fn main() {
|
||||
let _ = core::mem::size_of::<MissingSpec>();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: couldn't read `$DIR/tests/ui/fail_missing_sidecar.v0.1.0.spec.md`: No such file or directory (os error 2)
|
||||
--> tests/ui/fail_missing_sidecar.rs:3:1
|
||||
|
|
||||
3 | #[spec("fail_missing_sidecar.v0.1.0.spec.md")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -0,0 +1,9 @@
|
||||
use kaptan_spec::spec;
|
||||
|
||||
#[spec("flight_core_contract.v0.1.0.spec.md")]
|
||||
struct FlightCore;
|
||||
|
||||
fn main() {
|
||||
let size = core::mem::size_of::<FlightCore>();
|
||||
assert_eq!(size, 0);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: Flight core contract
|
||||
status: in-progress
|
||||
owners:
|
||||
- Test Name <name@test.com>
|
||||
---
|
||||
|
||||
# Summary
|
||||
|
||||
Minimal spec for compile-time linkage testing.
|
||||
Reference in new issue
Block a user