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
@@ -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