Skip to main content

surrealkit_macros/
lib.rs

1use std::env;
2use std::path::PathBuf;
3
4use proc_macro::TokenStream;
5use quote::quote;
6use syn::{LitStr, parse_macro_input};
7use walkdir::WalkDir;
8
9/// Embeds `.surql` schema files at compile time.
10///
11/// Generates a `pub mod embedded_schema` with a `SCHEMA` static and
12/// an async `sync(db)` function that applies all files to the database.
13///
14/// # Usage
15///
16/// ```rust,ignore
17/// surrealkit::embed_schema!();
18/// surrealkit::embed_schema!("database/schema");
19///
20/// embedded_schema::sync(&db).await?;
21/// ```
22#[proc_macro]
23pub fn embed_schema(input: TokenStream) -> TokenStream {
24	let schema_dir = if input.is_empty() {
25		"database/schema".to_string()
26	} else {
27		let lit = parse_macro_input!(input as LitStr);
28		lit.value()
29	};
30
31	let manifest_dir =
32		env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set during macro expansion");
33	let abs_schema_dir = PathBuf::from(&manifest_dir).join(&schema_dir);
34
35	if !abs_schema_dir.exists() {
36		panic!("embed_schema!: schema directory does not exist: {}", abs_schema_dir.display());
37	}
38
39	let mut file_paths: Vec<PathBuf> = WalkDir::new(&abs_schema_dir)
40		.follow_links(true)
41		.into_iter()
42		.filter_map(|e| e.ok())
43		.filter(|e| e.file_type().is_file())
44		.map(|e| e.into_path())
45		.filter(|p| p.extension().and_then(|s| s.to_str()) == Some("surql"))
46		.collect();
47	file_paths.sort();
48
49	let file_entries: Vec<_> = file_paths
50		.iter()
51		.map(|abs_path| {
52			let abs_str = abs_path.to_str().expect("non-UTF8 path in schema dir");
53			let rel = abs_path.strip_prefix(&abs_schema_dir).expect("path not under schema dir");
54			let rel_display =
55				format!("{}/{}", schema_dir, rel.to_str().unwrap()).replace('\\', "/");
56
57			quote! {
58				::surrealkit::EmbeddedSchemaFile {
59					path: #rel_display,
60					sql: include_str!(#abs_str),
61				}
62			}
63		})
64		.collect();
65
66	let expanded = quote! {
67		pub mod embedded_schema {
68			pub static SCHEMA: &[::surrealkit::EmbeddedSchemaFile] = &[
69				#(#file_entries),*
70			];
71
72			pub async fn sync(
73				db: &::surrealkit::Surreal<::surrealkit::engine::any::Any>,
74			) -> ::surrealkit::anyhow::Result<()> {
75				::surrealkit::Sync::embedded(SCHEMA).run(db).await
76			}
77		}
78	};
79
80	expanded.into()
81}