windjammer 0.48.0

A simple language inspired by Go, Ruby, and Elixir that transpiles to Rust - 80% of Rust's power with 20% of the complexity
Documentation
#![cfg(any(
    not(any(
        feature = "parser_tests",
        feature = "analyzer_tests",
        feature = "codegen_tests",
        feature = "interpreter_tests",
        feature = "conformance_tests",
        feature = "integration_tests",
    )),
    feature = "parser_tests",
))]

use std::fs;
use std::path::Path;

fn transpile_with_includes(source: &str, base_dir: &Path) -> Result<String, String> {
    let resolved = windjammer::wjsl::resolve_includes(source, base_dir, &mut Vec::new())
        .map_err(|e| e.to_string())?;
    windjammer::wjsl::transpile_wjsl(&resolved).map_err(|e| e.to_string())
}

#[test]
fn test_single_include() {
    let tmp = tempfile::tempdir().unwrap();
    let base = tmp.path();

    fs::write(
        base.join("camera.wjsl"),
        r#"struct CameraUniforms {
    view_matrix: mat4x4<f32>,
    position: vec3<f32>,
    _pad1: f32,
}
"#,
    )
    .unwrap();

    let source = r#"#include "camera.wjsl"

@group(0) @binding(0) uniform camera: CameraUniforms;

@compute @workgroup_size(8, 8, 1)
fn main(@builtin(global_invocation_id) id: vec3<u32>) {
    let pos = camera.position;
}
"#;

    let result = transpile_with_includes(source, base).unwrap();
    assert!(
        result.contains("CameraUniforms"),
        "Output should contain the included struct: {}",
        result
    );
    assert!(
        result.contains("view_matrix"),
        "Output should contain included struct fields: {}",
        result
    );
    assert!(
        !result.contains("#include"),
        "Output should NOT contain #include directive: {}",
        result
    );
}

#[test]
fn test_nested_include() {
    let tmp = tempfile::tempdir().unwrap();
    let base = tmp.path();

    fs::write(base.join("types.wjsl"), "struct Vec2F { x: f32, y: f32 }\n").unwrap();

    fs::write(
        base.join("camera.wjsl"),
        r#"#include "types.wjsl"
struct CameraUniforms {
    screen_size: vec2<f32>,
    near_plane: f32,
    far_plane: f32,
}
"#,
    )
    .unwrap();

    let source = r#"#include "camera.wjsl"

@group(0) @binding(0) uniform camera: CameraUniforms;

@compute @workgroup_size(8, 8, 1)
fn main(@builtin(global_invocation_id) id: vec3<u32>) {
    let near = camera.near_plane;
}
"#;

    let result = transpile_with_includes(source, base).unwrap();
    assert!(
        result.contains("Vec2F"),
        "Output should contain transitively included struct: {}",
        result
    );
    assert!(
        result.contains("CameraUniforms"),
        "Output should contain directly included struct: {}",
        result
    );
}

#[test]
fn test_missing_include_file() {
    let tmp = tempfile::tempdir().unwrap();
    let base = tmp.path();

    let source = r#"#include "nonexistent.wjsl"

@compute @workgroup_size(8, 8, 1)
fn main(@builtin(global_invocation_id) id: vec3<u32>) {}
"#;

    let result = transpile_with_includes(source, base);
    assert!(result.is_err(), "Should fail with missing include file");
    let err = result.unwrap_err();
    assert!(
        err.contains("nonexistent.wjsl"),
        "Error should mention the missing file: {}",
        err
    );
}

#[test]
fn test_circular_include_detection() {
    let tmp = tempfile::tempdir().unwrap();
    let base = tmp.path();

    fs::write(
        base.join("a.wjsl"),
        "#include \"b.wjsl\"\nstruct A { x: f32 }\n",
    )
    .unwrap();

    fs::write(
        base.join("b.wjsl"),
        "#include \"a.wjsl\"\nstruct B { y: f32 }\n",
    )
    .unwrap();

    let source = "#include \"a.wjsl\"\n";

    let result = windjammer::wjsl::resolve_includes(source, base, &mut Vec::new());
    assert!(result.is_err(), "Should detect circular include");
    let err = result.unwrap_err().to_string();
    assert!(
        err.contains("circular") || err.contains("Circular"),
        "Error should mention circular dependency: {}",
        err
    );
}

#[test]
fn test_duplicate_include_deduplicated() {
    let tmp = tempfile::tempdir().unwrap();
    let base = tmp.path();

    fs::write(base.join("types.wjsl"), "struct Shared { value: f32 }\n").unwrap();

    let source = r#"#include "types.wjsl"
#include "types.wjsl"

@group(0) @binding(0) uniform data: Shared;

@compute @workgroup_size(8, 8, 1)
fn main(@builtin(global_invocation_id) id: vec3<u32>) {
    let v = data.value;
}
"#;

    let result = transpile_with_includes(source, base).unwrap();
    let count = result.matches("Shared").count();
    assert!(
        count <= 3,
        "Struct Shared should not be duplicated excessively (found {} occurrences): {}",
        count,
        result
    );
}

#[test]
fn test_include_with_subdirectory() {
    let tmp = tempfile::tempdir().unwrap();
    let base = tmp.path();
    let subdir = base.join("wjsl_std");
    fs::create_dir_all(&subdir).unwrap();

    fs::write(
        subdir.join("camera.wjsl"),
        "struct CameraUniforms { position: vec3<f32>, _pad: f32 }\n",
    )
    .unwrap();

    let source = r#"#include "wjsl_std/camera.wjsl"

@group(0) @binding(0) uniform camera: CameraUniforms;

@compute @workgroup_size(8, 8, 1)
fn main(@builtin(global_invocation_id) id: vec3<u32>) {
    let p = camera.position;
}
"#;

    let result = transpile_with_includes(source, base).unwrap();
    assert!(
        result.contains("CameraUniforms"),
        "Should resolve includes from subdirectory: {}",
        result
    );
}

#[test]
fn test_no_includes_passthrough() {
    let source = r#"
@group(0) @binding(0) storage read_write output: array<f32>;
@group(0) @binding(1) uniform screen_size: vec2<u32>;

@compute @workgroup_size(8, 8, 1)
fn main(@builtin(global_invocation_id) id: vec3<u32>) {
    let width = screen_size.x;
    let height = screen_size.y;
    if (id.x >= width || id.y >= height) { return; }
    let idx = id.y * width + id.x;
    output[idx] = 1.0;
}
"#;

    let tmp = tempfile::tempdir().unwrap();
    let result = transpile_with_includes(source, tmp.path()).unwrap();
    assert!(
        result.contains("output"),
        "Should pass through source without includes: {}",
        result
    );
}

#[test]
fn test_use_syntax() {
    let tmp = tempfile::tempdir().unwrap();
    let base = tmp.path();

    fs::write(
        base.join("camera.wjsl"),
        r#"struct CameraUniforms {
    view_matrix: mat4x4<f32>,
    position: vec3<f32>,
    _pad1: f32,
}
"#,
    )
    .unwrap();

    let source = r#"use "camera.wjsl"

@group(0) @binding(0) uniform camera: CameraUniforms;

@compute @workgroup_size(8, 8, 1)
fn main(@builtin(global_invocation_id) id: vec3<u32>) {
    let pos = camera.position;
}
"#;

    let result = transpile_with_includes(source, base);
    assert!(
        result.is_ok(),
        "use syntax should work like #include: {:?}",
        result.err()
    );
    let wgsl = result.unwrap();
    assert!(
        wgsl.contains("CameraUniforms"),
        "use should inline the included file: {}",
        wgsl
    );
}

#[test]
fn test_use_and_include_both_work() {
    let tmp = tempfile::tempdir().unwrap();
    let base = tmp.path();

    fs::write(base.join("types_a.wjsl"), "struct TypeA { value: f32, }\n").unwrap();

    fs::write(base.join("types_b.wjsl"), "struct TypeB { count: u32, }\n").unwrap();

    let source = r#"use "types_a.wjsl"
#include "types_b.wjsl"

@group(0) @binding(0) uniform a: TypeA;
@group(0) @binding(1) uniform b: TypeB;

@compute @workgroup_size(8, 8, 1)
fn main(@builtin(global_invocation_id) id: vec3<u32>) {
    let v = a.value;
}
"#;

    let result = transpile_with_includes(source, base);
    assert!(
        result.is_ok(),
        "Mixed use/include should work: {:?}",
        result.err()
    );
    let wgsl = result.unwrap();
    assert!(wgsl.contains("TypeA"), "Should contain TypeA");
    assert!(wgsl.contains("TypeB"), "Should contain TypeB");
}