Skip to content

Commit 6ea5c73

Browse files
committed
Feature taxonomy, facade refactor, and API gate
Introduce a canonical feature taxonomy (core-*, protocol-*, extras-*) and migrate legacy feature aliases for compatibility. Refactor the rustapi-rs facade to expose stable namespaces (core, protocol, extras) and provide backward-compatible root aliases; update Cargo.toml features accordingly. Add CONTRACT.md to document the public contract and SemVer/MSRV/deprecation policies. Add committed public API snapshots (api/public/*) and a GitHub Actions workflow plus script to detect snapshot drift and require a `breaking` or `feature` PR label. Update templates, CLI, tests, and docs to use the new feature names. Make several internal API adjustments: seal the Handler trait, adjust module visibility/exports in rustapi-core, and update procedural macros to resolve internal crate paths via the facade's __private exports. Remove RELEASES.md and apply related README/architecture documentation tweaks.
1 parent f268d40 commit 6ea5c73

25 files changed

Lines changed: 702 additions & 539 deletions

File tree

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ Please include a summary of the changes and the related issue. Please also inclu
2424
- [ ] I have added tests that prove my fix is effective or that my feature works
2525
- [ ] New and existing unit tests pass locally with my changes
2626
- [ ] Any dependent changes have been merged and published in downstream modules
27+
- [ ] If `api/public/*` snapshots changed, this PR has `breaking` or `feature` label
2728

2829
## Related Issues
2930

@@ -44,4 +45,4 @@ Add screenshots to help explain your changes.
4445

4546
## Additional Notes
4647

47-
Add any other context about the pull request here.
48+
Add any other context about the pull request here.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
base_sha="${BASE_SHA:?BASE_SHA is required}"
5+
head_sha="${HEAD_SHA:?HEAD_SHA is required}"
6+
7+
changed_snapshots="$(
8+
git diff --name-only "$base_sha" "$head_sha" -- \
9+
api/public/rustapi-rs.default.txt \
10+
api/public/rustapi-rs.all-features.txt
11+
)"
12+
13+
if [[ -z "$changed_snapshots" ]]; then
14+
echo "No public API snapshot changes detected."
15+
exit 0
16+
fi
17+
18+
labels=",${PR_LABELS:-},"
19+
if [[ "$labels" == *",breaking,"* || "$labels" == *",feature,"* ]]; then
20+
echo "Public API snapshots changed and required label is present."
21+
exit 0
22+
fi
23+
24+
echo "::error::Public API snapshots changed but PR is missing required label: breaking or feature."
25+
echo "Changed snapshot files:"
26+
echo "$changed_snapshots"
27+
exit 1

.github/workflows/public-api.yml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: Public API
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
permissions:
10+
contents: read
11+
pull-requests: read
12+
13+
env:
14+
CARGO_TERM_COLOR: always
15+
16+
jobs:
17+
snapshot:
18+
name: Snapshot Drift
19+
runs-on: ubuntu-latest
20+
steps:
21+
- uses: actions/checkout@v4
22+
23+
- name: Install Rust
24+
uses: dtolnay/rust-toolchain@stable
25+
26+
- name: Install cargo-public-api
27+
uses: taiki-e/install-action@cargo-public-api
28+
29+
- name: Generate current snapshots
30+
run: |
31+
cargo public-api -p rustapi-rs -s > /tmp/rustapi-rs.default.txt
32+
cargo public-api -p rustapi-rs --all-features -s > /tmp/rustapi-rs.all-features.txt
33+
34+
- name: Check snapshot drift
35+
run: |
36+
diff -u api/public/rustapi-rs.default.txt /tmp/rustapi-rs.default.txt
37+
diff -u api/public/rustapi-rs.all-features.txt /tmp/rustapi-rs.all-features.txt
38+
39+
label-gate:
40+
name: Label Gate
41+
runs-on: ubuntu-latest
42+
needs: snapshot
43+
if: github.event_name == 'pull_request'
44+
steps:
45+
- uses: actions/checkout@v4
46+
with:
47+
fetch-depth: 0
48+
49+
- name: Enforce labels when snapshots change
50+
env:
51+
BASE_SHA: ${{ github.event.pull_request.base.sha }}
52+
HEAD_SHA: ${{ github.sha }}
53+
PR_LABELS: ${{ join(github.event.pull_request.labels.*.name, ',') }}
54+
run: |
55+
bash .github/scripts/public_api_label_gate.sh

CONTRACT.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# RustAPI Contract
2+
3+
This document defines compatibility guarantees for the RustAPI workspace.
4+
5+
## 1. Scope
6+
7+
- Stable public contract:
8+
- `rustapi-rs` (facade crate)
9+
- `cargo-rustapi` (CLI surface)
10+
- Internal implementation crates (best-effort, no stability guarantee):
11+
- `rustapi-core`, `rustapi-openapi`, `rustapi-validate`, `rustapi-macros`
12+
- `rustapi-extras`, `rustapi-ws`, `rustapi-toon`, `rustapi-view`, `rustapi-grpc`
13+
- `rustapi-testing`, `rustapi-jobs`
14+
15+
Do not depend on internal crate APIs for long-term compatibility.
16+
17+
## 2. SemVer Policy
18+
19+
- `rustapi-rs` follows strict SemVer:
20+
- Breaking changes: major
21+
- Additive changes: minor
22+
- Fixes/internal-only changes: patch
23+
- Public API surface is tracked by committed snapshots:
24+
- `api/public/rustapi-rs.default.txt`
25+
- `api/public/rustapi-rs.all-features.txt`
26+
- Pull requests that change snapshots must be labeled:
27+
- `breaking` (if compatibility breaks)
28+
- `feature` (if additive API surface change)
29+
30+
## 3. MSRV Policy
31+
32+
- Workspace MSRV is pinned to Rust `1.78`.
33+
- MSRV increases are allowed only in minor or major releases.
34+
- MSRV changes must be called out in changelog/release notes.
35+
- Patch releases must not raise MSRV.
36+
37+
## 4. Deprecation Policy
38+
39+
- Deprecations are soft-first:
40+
- `#[deprecated]` attribute
41+
- explicit migration path in docs/release notes
42+
- Minimum deprecation window before removal: 2 minor releases.
43+
- Removals occur only in major releases.
44+
45+
## 5. Feature Flag Policy
46+
47+
- New facade feature names must follow taxonomy:
48+
- `core-*`
49+
- `protocol-*`
50+
- `extras-*`
51+
- Meta features:
52+
- `core` (default)
53+
- `protocol-all`
54+
- `extras-all`
55+
- `full`
56+
- Legacy aliases may exist temporarily for migration but must be treated as deprecated and eventually removed on a published timeline.
57+
58+
## 6. Internal Leakage Rule
59+
60+
- Public facade signatures should not expose internal crate paths.
61+
- Macro/runtime internals are allowed only via `rustapi_rs::__private` and are excluded from stability guarantees.

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,22 @@ async fn list_users() -> &'static str { "ok" }
114114
***Multi-threaded Runtime**
115115
***Zero Config**
116116

117+
## Feature Taxonomy (Stable)
118+
119+
RustAPI now groups features into three namespaces:
120+
121+
| Namespace | Purpose | Examples |
122+
|:--|:--|:--|
123+
| `core-*` | Core framework capabilities | `core-openapi`, `core-tracing`, `core-http3` |
124+
| `protocol-*` | Optional protocol crates | `protocol-toon`, `protocol-ws`, `protocol-view`, `protocol-grpc` |
125+
| `extras-*` | Optional production middleware/integrations | `extras-jwt`, `extras-cors`, `extras-rate-limit`, `extras-replay` |
126+
127+
Meta features:
128+
- `core` (default)
129+
- `protocol-all`
130+
- `extras-all`
131+
- `full = core + protocol-all + extras-all`
132+
117133
## ✨ Latest Release Highlights (v0.1.335)
118134

119135
***Dual-Stack Runtime**: Simultaneous HTTP/1.1 (TCP) and HTTP/3 (QUIC/UDP) support

RELEASES.md

Lines changed: 0 additions & 48 deletions
This file was deleted.
28.1 KB
Binary file not shown.

api/public/rustapi-rs.default.txt

15.1 KB
Binary file not shown.

crates/cargo-rustapi/src/commands/new.rs

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,14 @@ pub async fn new_project(mut args: NewArgs) -> Result<()> {
9191
vec![]
9292
} else {
9393
let available = [
94-
"jwt",
95-
"cors",
96-
"rate-limit",
97-
"config",
98-
"toon",
99-
"ws",
100-
"view",
101-
"grpc",
94+
"extras-jwt",
95+
"extras-cors",
96+
"extras-rate-limit",
97+
"extras-config",
98+
"protocol-toon",
99+
"protocol-ws",
100+
"protocol-view",
101+
"protocol-grpc",
102102
];
103103
let defaults = match template {
104104
ProjectTemplate::Full => vec![true, true, true, true, false, false, false, false],
@@ -183,12 +183,10 @@ pub async fn new_project(mut args: NewArgs) -> Result<()> {
183183
style("http://localhost:8080").cyan()
184184
);
185185

186-
if features.iter().any(|f| f == "swagger-ui") || template == ProjectTemplate::Full {
187-
println!(
188-
"API docs available at {}",
189-
style("http://localhost:8080/docs").cyan()
190-
);
191-
}
186+
println!(
187+
"API docs available at {}",
188+
style("http://localhost:8080/docs").cyan()
189+
);
192190

193191
Ok(())
194192
}

crates/cargo-rustapi/src/templates/full.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ use tokio::fs;
77
pub async fn generate(name: &str, features: &[String]) -> Result<()> {
88
// Add recommended features for full template
99
let mut all_features: Vec<String> = vec![
10-
"jwt".to_string(),
11-
"cors".to_string(),
12-
"rate-limit".to_string(),
13-
"config".to_string(),
10+
"extras-jwt".to_string(),
11+
"extras-cors".to_string(),
12+
"extras-rate-limit".to_string(),
13+
"extras-config".to_string(),
1414
];
1515

1616
// Add user-specified features

0 commit comments

Comments
 (0)