glslint 0.8.0

A GLSL checker and language server for WebGL shader toolkits (luma.gl/deck.gl, maplibre, ShaderToy) and any project
//! glslint — a luma.gl/deck.gl-aware GLSL checker.
//!
//! Stock GLSL tools choke on shaders that reference module-injected symbols
//! (`wind.*`, `project_position_to_clipspace`). glslint assembles the modules +
//! deck builtins into a complete `#version 300 es` unit, validates it with the
//! Khronos glslangValidator reference compiler, and maps diagnostics back to the
//! original files.
//!
//!   glslint check FILE...   # one-shot validation (exit 1 on errors)
//!   glslint lsp             # language server over stdio (publishDiagnostics)

mod assemble;
mod check;
mod config;
mod deck;
mod derive;
mod diagnostics;
mod dialect;
mod discover;
mod drift;
mod embed;
mod include;
mod lints;
mod lsp;
mod preset;
mod symbols;

use diagnostics::{Severity, print_diag};
use std::path::Path;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let args: Vec<String> = std::env::args().skip(1).collect();
    match args.first().map(String::as_str) {
        Some("lsp") => lsp::run().await,
        Some("check") => {
            run_check(&args[1..]);
            Ok(())
        }
        Some("--version" | "-V") => {
            println!("glslint {}", env!("CARGO_PKG_VERSION"));
            Ok(())
        }
        _ => {
            eprintln!("usage: glslint <check FILE... | lsp>");
            std::process::exit(2);
        }
    }
}

fn run_check(files: &[String]) {
    if files.is_empty() {
        eprintln!("glslint check: no files given");
        std::process::exit(2);
    }

    let mut errors = 0usize;
    let mut warnings = 0usize;
    let mut notes = 0usize;

    for f in files {
        match check::check_file(Path::new(f)) {
            Ok(diags) => {
                for d in &diags {
                    print_diag(d);
                    match d.severity {
                        Severity::Error => errors += 1,
                        Severity::Warning => warnings += 1,
                        Severity::Note => notes += 1,
                    }
                }
            }
            Err(e) => {
                eprintln!("{f}: {e}");
                errors += 1;
            }
        }
    }

    if notes > 0 {
        eprintln!("\nglslint: {errors} error(s), {warnings} warning(s), {notes} note(s)");
    } else {
        eprintln!("\nglslint: {errors} error(s), {warnings} warning(s)");
    }
    if errors > 0 {
        std::process::exit(1);
    }
}