# qa
Requirements traceability for safety-critical Rust software. Define requirements as code, link them to tests with an attribute macro, and auto-generate a traceability matrix.
## Quick Start
```toml
# Cargo.toml
[dependencies]
qa = "0.1"
```
```rust
use qa::RequirementType::*;
// Define a requirement
qa::requirement! {
pub REQ_ENERGY_CONSERVATION: Safety {
description: "Total energy must be conserved across transport events",
source: "NRC Reg Guide 1.190 §3.1",
}
}
// Link a test to it
#[test]
#[qa::traces(REQ_ENERGY_CONSERVATION)]
fn test_energy_is_conserved() {
// ...
}
// Generate the matrix
#[test]
fn traceability_matrix() {
let matrix = qa::Matrix::collect();
matrix.print();
matrix.write("traceability.md", qa::Format::Markdown).unwrap();
matrix.assert_complete(); // panics if any requirement has no tests
}
```
## Defining Requirements
### Single requirement
```rust
qa::requirement! {
pub REQ_VOID_TRACKING: Safety {
description: "Particles must be tracked through void regions without energy loss",
source: "NRC Reg Guide 1.190 §4.2",
priority: "critical",
}
}
```
### Batch definition
```rust
qa::requirements! {
pub REQ_VOID_TRACKING: Safety {
description: "Particles must be tracked through void regions without energy loss",
source: "NRC Reg Guide 1.190 §4.2",
}
pub REQ_ENERGY_CONSERVATION: Safety {
description: "Total energy must be conserved across transport events",
source: "NRC Reg Guide 1.190 §3.1",
}
pub REQ_XS_INTERPOLATION: Functional {
description: "Cross-section lookup must interpolate between tabulated energy points",
source: "ENDF/B-VIII.0 format manual",
}
}
```
### Requirement fields
- **`description`** (required, must be first): human-readable text
- **type** (required, after `:`): a `RequirementType` variant
- All other key-value pairs become freeform metadata
### RequirementType
Built-in variants: `Functional`, `NonFunctional`, `Performance`, `Safety`, `Security`, `Interface`.
Extend with `Custom`:
```rust
const REGULATORY: qa::RequirementType = qa::RequirementType::Custom("regulatory");
qa::requirement! {
pub REQ_NRC_COMPLIANCE: REGULATORY {
description: "System must comply with NRC regulations",
}
}
```
### Accessing metadata
```rust
let source = REQ_VOID_TRACKING.get("source"); // Some("NRC Reg Guide 1.190 §4.2")
let missing = REQ_VOID_TRACKING.get("nonexistent"); // None
```
## Tracing Tests
```rust
use qa::traces;
#[test]
#[traces(REQ_VOID_TRACKING)]
fn test_void_traversal() { /* ... */ }
// Multiple requirements per test
#[test]
#[traces(REQ_VOID_TRACKING, REQ_ENERGY_CONSERVATION)]
fn test_energy_through_void() { /* ... */ }
```
### Composability
`#[traces]` emits the test function unchanged and appends registration as a separate item. It composes with any attribute macro regardless of ordering:
```rust
#[rstest]
#[traces(REQ_FIXTURES)]
fn test_with_fixture(fixture: MyFixture) { /* ... */ }
#[test]
#[should_panic(expected = "negative energy")]
#[traces(REQ_ERROR_HANDLING)]
fn test_negative_energy_panics() { /* ... */ }
```
## Generating the Matrix
```rust
let matrix = qa::Matrix::collect();
```
### Analysis
```rust
matrix.requirements() // all registered requirements
matrix.traces() // all registered traces
matrix.traces_for("REQ_VOID_TRACKING") // tests for a specific requirement
matrix.untraced() // requirements with no tests
matrix.orphan_traces() // traces referencing nonexistent requirement IDs
matrix.coverage() // fraction of requirements with ≥1 test (0.0..=1.0)
matrix.assert_complete() // panics if any requirement is untraced
```
### Output
```rust
// Terminal table (via tabled)
matrix.print();
// Markdown file
matrix.write("traceability.md", qa::Format::Markdown).unwrap();
// JSON file (requires `json` feature)
matrix.write("traceability.json", qa::Format::Json).unwrap();
```
## Organizing Requirements
Place requirements at the top of the files they govern:
```rust
// src/transport.rs
qa::requirements! {
pub REQ_VOID_TRACKING: Safety {
description: "Particles must be tracked through void regions without energy loss",
source: "NRC Reg Guide 1.190 §4.2",
}
pub REQ_ENERGY_CONSERVATION: Safety {
description: "Total energy must be conserved across transport events",
source: "NRC Reg Guide 1.190 §3.1",
}
}
// ... implementation code ...
```
Requirements register globally via `linkme` distributed slices regardless of which file or crate they're defined in. No central manifest to maintain.
## Filtering and Grouping
Operate on freeform metadata for custom views:
```rust
// Only safety requirements
let safety = matrix.filter_by("priority", "critical");
safety.print();
// Group by source document
let by_source = matrix.group_by("source");
for (source, sub_matrix) in &by_source {
println!("{}: {} requirements", source, sub_matrix.requirements().len());
}
```
## `no_std` Support
The core types, macros, and `linkme` slices work without `std`. Disable default features:
```toml
[dependencies]
qa = { version = "0.1", default-features = false }
```
This gives you `Requirement`, `RequirementType`, `TestTrace`, `requirement!`, `requirements!`, and `#[traces]` on bare-metal targets. `Matrix` and all rendering require `std`.
Typical workflow: define requirements in your `no_std` library crate, then collect and render the matrix from a host-side test binary that enables `std`.
## Feature Flags
| `std` | yes | `Matrix`, terminal tables, file output |
| `json` | no | JSON rendering via serde (implies `std`) |
## Workspace
| `qa` | Core library — types, macros, matrix generation |
| `qa-macros` | Proc-macro crate — `#[traces]` attribute |
## Development
```sh
cargo test --workspace # all tests
cargo test --workspace --features json # with JSON output
cargo clippy --workspace --all-targets -- -D warnings
```
### no_std verification
```sh
rustup target add thumbv7em-none-eabihf
cargo build -p qa --no-default-features --target thumbv7em-none-eabihf
```
### Releasing
Releases are triggered manually from GitLab CI. Go to CI/CD → Pipelines → Run pipeline, set the `BUMP` variable to `patch`, `minor`, or `major`, then run the `release` job. This bumps versions across the workspace, publishes to crates.io, and pushes a version commit + tag.
## License
MIT