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 = "integration_tests",
))]

// TDD Tests for Nested Module System - The Windjammer Way!
//
// Bug discovered: Windjammer doesn't support nested module structures
// like game engines need (math/vec2.wj, rendering/color.wj, etc.)
//
// The Windjammer Philosophy:
// - Compiler does the work, not the developer
// - Auto-discover modules from directory structure
// - Respect explicit pub mod / pub use declarations
// - Generate correct lib.rs for libraries
//
// This is NOT just copying Rust - it's the Windjammer way!

use std::fs;
use tempfile::TempDir;

/// Helper: Create a test directory structure
fn create_test_project(files: &[(&str, &str)]) -> TempDir {
    let temp_dir = TempDir::new().unwrap();

    for (path, content) in files {
        let full_path = temp_dir.path().join(path);

        // Create parent directories
        if let Some(parent) = full_path.parent() {
            fs::create_dir_all(parent).unwrap();
        }

        fs::write(&full_path, content).unwrap();
    }

    temp_dir
}

#[cfg(test)]
mod module_discovery_tests {
    use super::*;

    #[test]
    fn test_discover_flat_modules() {
        // Flat structure: src/vec2.wj, src/vec3.wj
        let _temp_dir = create_test_project(&[
            ("vec2.wj", "pub struct Vec2 { pub x: f64, pub y: f64 }"),
            (
                "vec3.wj",
                "pub struct Vec3 { pub x: f64, pub y: f64, pub z: f64 }",
            ),
        ]);

        // TODO: Implement discover_modules function
        // let modules = windjammer::module_system::discover_modules(temp_dir.path()).unwrap();

        // assert_eq!(modules.len(), 2);
        // assert!(modules.contains_key("vec2"));
        // assert!(modules.contains_key("vec3"));
    }

    #[test]
    fn test_discover_nested_modules() {
        // Nested structure:
        // src/
        //   mod.wj
        //   math/
        //     mod.wj
        //     vec2.wj
        //     vec3.wj
        //   rendering/
        //     color.wj
        let _temp_dir = create_test_project(&[
            ("mod.wj", "pub mod math\npub mod rendering"),
            ("math/mod.wj", "pub mod vec2\npub mod vec3"),
            ("math/vec2.wj", "pub struct Vec2 { pub x: f64, pub y: f64 }"),
            (
                "math/vec3.wj",
                "pub struct Vec3 { pub x: f64, pub y: f64, pub z: f64 }",
            ),
            (
                "rendering/color.wj",
                "pub struct Color { pub r: u8, pub g: u8, pub b: u8 }",
            ),
        ]);

        // TODO: Implement discover_nested_modules function
        // let module_tree = windjammer::module_system::discover_nested_modules(temp_dir.path()).unwrap();

        // Should discover:
        // - math (directory module)
        //   - vec2 (file submodule)
        //   - vec3 (file submodule)
        // - rendering (directory module)
        //   - color (file submodule - no mod.wj, auto-discovered!)

        // assert_eq!(module_tree.root_modules.len(), 2);
        // assert!(module_tree.has_module(&["math"]));
        // assert!(module_tree.has_module(&["math", "vec2"]));
        // assert!(module_tree.has_module(&["math", "vec3"]));
        // assert!(module_tree.has_module(&["rendering", "color"]));
    }

    #[test]
    fn test_auto_discover_without_mod_wj() {
        // Windjammer Way: Auto-discover modules even without mod.wj!
        // src/
        //   math/  (no mod.wj!)
        //     vec2.wj
        //     vec3.wj
        let _temp_dir = create_test_project(&[
            ("math/vec2.wj", "pub struct Vec2 { pub x: f64, pub y: f64 }"),
            (
                "math/vec3.wj",
                "pub struct Vec3 { pub x: f64, pub y: f64, pub z: f64 }",
            ),
        ]);

        // TODO: Should auto-discover math/ as a module!
        // let module_tree = windjammer::module_system::discover_nested_modules(temp_dir.path()).unwrap();

        // assert!(module_tree.has_module(&["math"]));
        // assert!(module_tree.has_module(&["math", "vec2"]));
        // assert!(module_tree.has_module(&["math", "vec3"]));
    }
}

#[cfg(test)]
mod lib_rs_generation_tests {
    use super::*;

    #[test]
    fn test_generate_lib_rs_flat() {
        // Flat structure should generate simple lib.rs
        let _temp_dir = create_test_project(&[
            ("vec2.wj", "pub struct Vec2 { pub x: f64, pub y: f64 }"),
            (
                "vec3.wj",
                "pub struct Vec3 { pub x: f64, pub y: f64, pub z: f64 }",
            ),
        ]);

        // TODO: Implement generate_lib_rs function
        // let lib_rs = windjammer::module_system::generate_lib_rs(temp_dir.path()).unwrap();

        // Should generate:
        // pub mod vec2;
        // pub mod vec3;
        // pub use vec2::*;
        // pub use vec3::*;

        // assert!(lib_rs.contains("pub mod vec2;"));
        // assert!(lib_rs.contains("pub mod vec3;"));
        // assert!(lib_rs.contains("pub use vec2::*;") || lib_rs.contains("pub use vec2::Vec2;"));
    }

    #[test]
    fn test_generate_lib_rs_nested() {
        // Nested structure should generate hierarchical lib.rs
        let _temp_dir = create_test_project(&[
            ("mod.wj", "pub mod math\npub mod rendering"),
            ("math/mod.wj", "pub mod vec2\npub mod vec3"),
            ("math/vec2.wj", "pub struct Vec2 { pub x: f64, pub y: f64 }"),
            (
                "math/vec3.wj",
                "pub struct Vec3 { pub x: f64, pub y: f64, pub z: f64 }",
            ),
            (
                "rendering/color.wj",
                "pub struct Color { pub r: u8, pub g: u8, pub b: u8 }",
            ),
        ]);

        // TODO: Should generate lib.rs that declares top-level modules
        // let lib_rs = windjammer::module_system::generate_lib_rs(temp_dir.path()).unwrap();

        // Should generate (simplified):
        // pub mod math;
        // pub mod rendering;

        // assert!(lib_rs.contains("pub mod math;"));
        // assert!(lib_rs.contains("pub mod rendering;"));

        // math/mod.rs should be generated separately with:
        // pub mod vec2;
        // pub mod vec3;
    }

    #[test]
    fn test_preserve_pub_use_from_mod_wj() {
        // THE CRITICAL TEST - Respect explicit declarations!
        let _temp_dir = create_test_project(&[
            ("mod.wj", "pub mod math\npub use math::Vec2\npub use math::Vec3"),
            ("math/mod.wj", "pub struct Vec2 { pub x: f64, pub y: f64 }\npub struct Vec3 { pub x: f64, pub y: f64, pub z: f64 }"),
        ]);

        // TODO: Should preserve pub use declarations from mod.wj
        // let lib_rs = windjammer::module_system::generate_lib_rs(temp_dir.path()).unwrap();

        // Should generate:
        // pub mod math;
        // pub use math::Vec2;
        // pub use math::Vec3;

        // NOT: pub use math::*; (too broad!)

        // assert!(lib_rs.contains("pub mod math;"));
        // assert!(lib_rs.contains("pub use math::Vec2;"));
        // assert!(lib_rs.contains("pub use math::Vec3;"));
        // assert!(!lib_rs.contains("pub use math::*;"), "Should NOT use wildcard re-exports when explicit pub use exists");
    }
}

#[cfg(test)]
mod integration_tests {
    use super::*;

    #[test]
    fn test_compile_game_engine_structure() {
        // Realistic game engine structure like windjammer-game!
        let _temp_dir = create_test_project(&[
            (
                "mod.wj",
                r#"
pub mod math
pub mod rendering
pub mod physics

pub use math::Vec2
pub use math::Vec3
pub use rendering::Color
pub use physics::RigidBody2D
"#,
            ),
            ("math/mod.wj", "pub mod vec2\npub mod vec3"),
            ("math/vec2.wj", "pub struct Vec2 { pub x: f64, pub y: f64 }"),
            (
                "math/vec3.wj",
                "pub struct Vec3 { pub x: f64, pub y: f64, pub z: f64 }",
            ),
            (
                "rendering/color.wj",
                "pub struct Color { pub r: u8, pub g: u8, pub b: u8 }",
            ),
            (
                "physics/rigidbody2d.wj",
                "pub struct RigidBody2D { pub mass: f64 }",
            ),
        ]);

        // TODO: Full compilation pipeline
        // let output = windjammer::compile_project(temp_dir.path(), target_dir).unwrap();

        // Should generate:
        // - lib.rs (top-level with pub mod + explicit pub use)
        // - math/mod.rs (pub mod vec2; pub mod vec3;)
        // - math/vec2.rs
        // - math/vec3.rs
        // - rendering/color.rs
        // - physics/rigidbody2d.rs

        // And lib.rs should have:
        // pub mod math;
        // pub mod rendering;
        // pub mod physics;
        // pub use math::Vec2;
        // pub use math::Vec3;
        // pub use rendering::Color;
        // pub use physics::RigidBody2D;
    }

    #[test]
    fn test_windjammer_vs_rust_comparison() {
        // This test documents the Windjammer philosophy!

        // RUST WAY (manual declarations everywhere):
        // src/lib.rs:
        //   pub mod math;
        // src/math/mod.rs:
        //   pub mod vec2;
        //   pub mod vec3;
        // src/math/vec2.rs:
        //   pub struct Vec2 { ... }

        // WINDJAMMER WAY (auto-discover + smart defaults):
        // src/mod.wj:
        //   pub mod math  // optional! auto-discovered if directory exists
        //   pub use math::Vec2  // explicit re-export (matters!)
        //
        // Compiler generates everything else automatically!

        // The difference: Windjammer infers structure, Rust forces declaration
        // This is consistent with Windjammer's philosophy of inferring what doesn't matter
    }
}

#[cfg(test)]
mod regression_tests {
    use super::*;

    #[test]
    fn test_flat_structure_still_works() {
        // Ensure we don't break existing flat projects!
        let _temp_dir = create_test_project(&[
            ("main.wj", "fn main() { println(\"Hello\") }"),
            ("utils.wj", "pub fn helper() -> i32 { 42 }"),
        ]);

        // TODO: Should still generate mod.rs for flat projects
        // (This is the existing behavior - don't break it!)
    }
}

#[cfg(test)]
mod file_placement_tests {

    #[test]
    fn test_preserve_directory_structure_in_output() {
        // When compiling nested modules, output .rs files should maintain
        // the same directory structure as source .wj files
        //
        // Input: src/math/vec2.wj
        // Output: build/math/vec2.rs (NOT build/vec2.rs!)

        // This test validates that get_relative_output_path() works correctly
        // TODO: Implement get_relative_output_path(source_root, input_file, output_root)
    }

    #[test]
    fn test_output_path_calculation() {
        // source_root = "src/"
        // input_file = "src/math/vec2.wj"
        // output_root = "build/"
        // Expected: "build/math/vec2.rs"

        // source_root = "src/"
        // input_file = "src/rendering/color.wj"
        // output_root = "build/"
        // Expected: "build/rendering/color.rs"
    }
}