diff --git a/Cargo.toml b/Cargo.toml index 38004fd..496a3c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,4 +8,5 @@ proc-macro = true [dependencies] quote = "1.0" -syn = "2.0.90" \ No newline at end of file +syn = "2.0.90" +naga = { version = "23.0.0", features = ["wgsl-in"] } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index e7e7a05..94b0cc2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,24 +1,68 @@ #![feature(proc_macro_span)] use { + naga::front::wgsl, proc_macro::TokenStream, quote::quote, - std::{fs, path::PathBuf}, + std::fs, 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)), - } +struct ShaderModule { + source_file: std::path::PathBuf, + source_text: String, + naga_module: naga::Module, +} + +impl ShaderModule { + fn try_new(source_file: impl Into) -> Result { + let source_file = source_file.into(); + let source_text = fs::read_to_string(&source_file).map_err(|err| { + Error::from_message(format!( + "Could not open \"{}\": {}", + &source_file.as_os_str().to_string_lossy(), + err + )) + })?; + + let naga_module = wgsl::parse_str(&source_text).map_err(|err| { + if let Some(location) = err.location(&source_text) { + Error::Message(format!( + "Error in {} at line {}, column {}:\n{}", + &source_file.as_os_str().to_string_lossy(), + location.line_number, + location.line_position, + err.message() + )) + } else { + Error::Message(format!( + "Error in {}:\n{}", + &source_file.as_os_str().to_string_lossy(), + err.message() + )) + } + })?; + Ok(Self { + source_file, + source_text, + naga_module, + }) + } + + fn emit_rust_code(&self) -> TokenStream { + let source_text = &self.source_text; + quote! { + wgpu::ShaderModuleDescriptor { + label: None, + source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(#source_text)), + } + } + .into() } - .into() } struct WgslModuleMacroInput { - source_dir: PathBuf, + source_dir: std::path::PathBuf, filename: String, } @@ -58,20 +102,17 @@ fn parse_input(token_stream: TokenStream) -> Result 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)) + let module = ShaderModule::try_new(full_filename)?; + Ok(module.emit_rust_code()) } #[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()) + 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() + }) }