From 395e6412506129e0ec2ef391d343cfdfa9657f9d Mon Sep 17 00:00:00 2001 From: Matthew Gordon Date: Sat, 7 Dec 2024 12:08:28 -0400 Subject: [PATCH] Create proc macro for loading single WGSL file Now to add features. --- Cargo.toml | 11 +++++++ rust-toolchain.toml | 2 ++ src/lib.rs | 77 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 Cargo.toml create mode 100644 rust-toolchain.toml create mode 100644 src/lib.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..38004fd --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "wgsl-shader-assembler" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +quote = "1.0" +syn = "2.0.90" \ No newline at end of file diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..271800c --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..e7e7a05 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,77 @@ +#![feature(proc_macro_span)] + +use { + proc_macro::TokenStream, + quote::quote, + std::{fs, path::PathBuf}, + syn::{parse, LitStr}, +}; + +fn emit_create_shader_module(file_contents: &str) -> TokenStream { + quote! { + wgpu::ShaderModuleDescriptor { + label: None, + source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(#file_contents)), + } + } + .into() +} + +struct WgslModuleMacroInput { + source_dir: PathBuf, + filename: String, +} + +enum Error { + Message(String), + SynError(syn::Error), +} + +impl Error { + fn from_message(message: impl Into) -> Self { + Self::Message(message.into()) + } +} + +impl From for Error { + fn from(value: syn::Error) -> Self { + Self::SynError(value) + } +} + +fn parse_input(token_stream: TokenStream) -> Result { + let token = token_stream + .clone() + .into_iter() + .next() + .ok_or(Error::from_message("Expected single string literal."))?; + + let source_dir = token.span().source_file().path().parent().unwrap().into(); + let literal: LitStr = parse(token_stream)?; + let filename = literal.value(); + Ok(WgslModuleMacroInput { + source_dir, + filename, + }) +} + +fn wgsl_module_inner(input_token_stream: TokenStream) -> Result { + let input = parse_input(input_token_stream)?; + let full_filename = input.source_dir.join(&input.filename); + let contents = fs::read_to_string(&full_filename).map_err(|err| { + Error::from_message(format!( + "Could not open \"{}\": {}", + &full_filename.as_os_str().to_string_lossy(), + err.to_string() + )) + })?; + Ok(emit_create_shader_module(&contents)) +} + +#[proc_macro] +pub fn wgsl_module(input: TokenStream) -> TokenStream { + wgsl_module_inner(input).unwrap_or_else(|err| match err { + Error::Message(message) => quote! { compile_error!(#message) }, + Error::SynError(syn_err) => syn_err.into_compile_error(), + }.into()) +}