imgui-ash 0.1.0

ash/VMA renderer for imgui-rs
Documentation
use build_print::{error, info, note, warn};
use shaderc::ShaderKind;
use std::{
    env,
    ffi::OsString,
    fs,
    io::Write,
    path::{Path, PathBuf},
};

fn main() -> Result<(), terminator::Terminator> {
    println!("cargo::rerun-if-changed=shaders");

    let out_dir: PathBuf = env::var("OUT_DIR")?.into();

    let shader_compiler = ShaderCompiler::new()?;
    for entry in fs::read_dir("shaders")? {
        let path = entry?.path();
        if !path.is_file() {
            note!(
                "shader-compile: skipping `{}` as it is a directory",
                path.display()
            );
            continue;
        }
        if let Err(e) = shader_compiler.compile_shader(&path, &out_dir) {
            if matches!(e, BuildError::UnsupportedExt(_) | BuildError::NotFile) {
                warn!("{e}");
                continue;
            }
            Err(e)?;
        }
    }
    Ok(())
}

pub struct ShaderCompiler {
    compiler: shaderc::Compiler,
}

impl ShaderCompiler {
    pub fn new() -> Result<Self, BuildError> {
        Ok(ShaderCompiler {
            compiler: shaderc::Compiler::new().map_err(BuildError::FailedInit)?,
        })
    }

    pub fn compile_shader(&self, path: &Path, out_dir: &Path) -> Result<(), BuildError> {
        let file_name = path
            .file_name()
            .ok_or(BuildError::NotFile)?
            .to_str()
            .expect("utf8 file name for shader");
        let ext = path
            .extension()
            .ok_or_else(|| BuildError::UnsupportedExt(path.to_owned().into_os_string()))?;
        let shader_kind = match ext.as_encoded_bytes() {
            b"vert" => ShaderKind::Vertex,
            b"frag" => ShaderKind::Fragment,
            _ => return Err(BuildError::UnsupportedExt(ext.to_owned())),
        };
        let src = fs::read_to_string(path)?;
        let compiled =
            self.compiler
                .compile_into_spirv(&src, shader_kind, file_name, "main", None)?;
        let output_path = out_dir.join(format!("{file_name}.spv"));
        let mut output_file = fs::File::create(output_path)?;
        output_file.write_all(compiled.as_binary_u8())?;
        info!("Compiled {} -> {}", file_name, format!("{file_name}.spv"));
        Ok(())
    }
}

#[derive(Debug, thiserror::Error)]
pub enum BuildError {
    #[error("Failed to initialise shader compiler")]
    FailedInit(#[source] shaderc::Error),
    #[error("I/O error when compiling shader")]
    Io(
        #[from]
        #[source]
        std::io::Error,
    ),
    #[error("unsupported shader extension: {}", _0.to_str().unwrap())]
    UnsupportedExt(OsString),
    #[error("source file is not a file")]
    NotFile,
    #[error("failed to compile shader")]
    CompileError(
        #[from]
        #[source]
        shaderc::Error,
    ),
}