chore(checkpoint): save current v0 build state
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "slhx-derive"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
quote = "1"
|
||||
syn = { version = "2", features = ["full"] }
|
||||
@@ -0,0 +1,110 @@
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use std::path::PathBuf;
|
||||
use syn::{parse_macro_input, ItemFn};
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn handler(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let function = parse_macro_input!(item as ItemFn);
|
||||
let name = function.sig.ident.to_string();
|
||||
|
||||
if let Some(syms_path) = syms_path() {
|
||||
if syms_path.exists() && !syms_contains_handle(&syms_path, &name) {
|
||||
let message = format!(
|
||||
"unknown slhx handle `{name}`; add `data-slhx-handle=\"{name}\"` to a template or rename this handler"
|
||||
);
|
||||
return quote!(
|
||||
#function
|
||||
compile_error!(#message);
|
||||
)
|
||||
.into();
|
||||
}
|
||||
}
|
||||
|
||||
quote!(#function).into()
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn surface(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
inject_surface_include(item)
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn component(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
item
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn app(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
item
|
||||
}
|
||||
|
||||
fn inject_surface_include(item: TokenStream) -> TokenStream {
|
||||
let item = item.to_string();
|
||||
let Some(insert_at) = item.rfind('}') else {
|
||||
return compile_error("#[slhx::surface] must be used on an inline module");
|
||||
};
|
||||
|
||||
let include = surface_include();
|
||||
let expanded = format!("{}{}{}", &item[..insert_at], include, &item[insert_at..]);
|
||||
expanded
|
||||
.parse()
|
||||
.unwrap_or_else(|_| compile_error("#[slhx::surface] could not expand this module"))
|
||||
}
|
||||
|
||||
fn surface_include() -> String {
|
||||
let Some(path) = generated_path("slhx.generated.rs") else {
|
||||
return String::new();
|
||||
};
|
||||
|
||||
if path.exists() {
|
||||
format!(" include!({:?}); ", path.display().to_string())
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn syms_path() -> Option<PathBuf> {
|
||||
generated_path("slhx.syms")
|
||||
}
|
||||
|
||||
fn generated_path(file: &str) -> Option<PathBuf> {
|
||||
std::env::var_os("OUT_DIR").map(|out_dir| PathBuf::from(out_dir).join(file))
|
||||
}
|
||||
|
||||
fn syms_contains_handle(path: &PathBuf, ident: &str) -> bool {
|
||||
let Ok(syms) = std::fs::read_to_string(path) else {
|
||||
return true;
|
||||
};
|
||||
syms.lines().any(|line| {
|
||||
let mut fields = line.split('\t');
|
||||
matches!(fields.next(), Some("handle"))
|
||||
&& fields.nth(1).is_some_and(|handle_ident| handle_ident == ident)
|
||||
})
|
||||
}
|
||||
|
||||
fn compile_error(message: &str) -> TokenStream {
|
||||
format!("compile_error!({message:?});")
|
||||
.parse()
|
||||
.expect("compile_error expansion is valid")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::syms_contains_handle;
|
||||
|
||||
#[test]
|
||||
fn syms_lookup_matches_handle_ident() {
|
||||
let path = std::env::temp_dir().join("slhx-derive-syms-test.syms");
|
||||
std::fs::write(
|
||||
&path,
|
||||
"slhx-syms-v1\nslot\ttemplates/a.heml::count\tcount\t1\nhandle\ttemplates/a.heml::create\tcreate\t2\n",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert!(syms_contains_handle(&path, "create"));
|
||||
assert!(!syms_contains_handle(&path, "missing"));
|
||||
|
||||
let _ = std::fs::remove_file(path);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
use slhx_derive::surface;
|
||||
|
||||
#[surface]
|
||||
mod ui {
|
||||
pub const EXISTING: u8 = 1;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn surface_macro_preserves_inline_module_without_generated_file() {
|
||||
assert_eq!(ui::EXISTING, 1);
|
||||
}
|
||||
Reference in New Issue
Block a user