splice 2.6.4

Span-safe refactoring kernel for 7 languages with Magellan code graph integration
Documentation
//! Command handler functions

use crate::cli::OutputFormat;
use crate::create::{self};
use crate::error::Result;
use std::io::Read;
use std::path::Path;

/// Handle `splice create` command
///
/// # Arguments
/// * `file_path` - Path where file should be created
/// * `validate_only` - If true, validate but don't write file
/// * `with_mod` - If true, add module declaration to parent module
/// * `workspace_dir` - Workspace directory
/// * `json_output` - Whether to output JSON format
///
/// # Returns
/// * `Ok(())` - Command executed successfully
/// * `Err(SpliceError)` - Command failed
pub fn cmd_create(
    file_path: &Path,
    validate_only: bool,
    with_mod: bool,
    workspace_dir: &Path,
    json_output: bool,
) -> Result<()> {
    // Convert json_output bool to OutputFormat
    let output_format = if json_output {
        OutputFormat::Json
    } else {
        OutputFormat::Human
    };
    // Step 1: Read code from stdin
    let mut code = String::new();
    std::io::stdin().read_to_string(&mut code)?;

    // Step 2: Call create module
    let result = if with_mod {
        create::create_file_with_module(file_path, &code, workspace_dir, true)
    } else {
        create::create_file_with_validation(file_path, &code, workspace_dir, validate_only)
    };

    // Step 3: Handle result
    match result {
        Ok(validation) => {
            // Output based on format
            match output_format {
                OutputFormat::Human => {
                    if validation.is_valid {
                        println!("✅ Code validation passed");
                        if !validate_only {
                            println!("✅ File created: {}", file_path.display());
                        } else {
                            println!("✅ Validation passed (file not written)");
                        }
                    } else {
                        println!("❌ Code validation failed");
                        for error in &validation.errors {
                            println!("  Error at line {}: {}", error.line, error.message);
                        }
                    }
                }
                OutputFormat::Json => {
                    let json = serde_json::to_string_pretty(&validation).map_err(|e| {
                        crate::error::SpliceError::IoContext {
                            context: "Failed to serialize JSON".to_string(),
                            source: std::io::Error::new(std::io::ErrorKind::InvalidData, e),
                        }
                    })?;
                    println!("{}", json);
                }
                OutputFormat::Pretty => {
                    let json = serde_json::to_string_pretty(&validation).map_err(|e| {
                        crate::error::SpliceError::IoContext {
                            context: "Failed to serialize JSON".to_string(),
                            source: std::io::Error::new(std::io::ErrorKind::InvalidData, e),
                        }
                    })?;
                    println!("{}", json);
                }
            }
            Ok(())
        }
        Err(e) => {
            match output_format {
                OutputFormat::Human => {
                    println!("❌ Error: {}", e);
                }
                OutputFormat::Json | OutputFormat::Pretty => {
                    use serde_json::json;
                    let error_json = json!({
                        "error": e.to_string(),
                        "valid": false
                    });
                    match serde_json::to_string_pretty(&error_json) {
                        Ok(json) => println!("{}", json),
                        Err(_) => println!("{}", json!({"error": e.to_string(), "valid": false})),
                    }
                }
            }
            Err(e)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::Cursor;
    use std::io::Write;
    use std::path::PathBuf;
    use tempfile::TempDir;

    /// Test that cmd_create handler function exists and has correct signature
    #[test]
    fn test_cmd_create_handler_exists() {
        // This test just verifies the function exists
        // Actual functionality is tested in integration tests
        use crate::commands::cmd_create;

        let temp_dir = TempDir::new().unwrap();
        let file_path = temp_dir.path().join("test.rs");
        let workspace = temp_dir.path();

        // Call with validate_only=true to test validation mode
        let code = r#"pub fn test() -> i32 { 42 }"#;
        let mock_stdin = Cursor::new(code.as_bytes());

        // Note: This test just checks the function compiles
        // Real testing is done in integration tests with actual stdin
        let _ = (file_path, workspace, mock_stdin);
    }

    /// Test cmd_create reads from stdin
    #[test]
    fn test_cmd_create_reads_stdin() {
        use std::io::Cursor;

        // Create a mock stdin
        let code = r#"pub fn test() -> i32 { 42 }"#;
        let mut mock_stdin = Cursor::new(code.as_bytes());

        // Verify we can read from it
        let mut buffer = String::new();
        mock_stdin.read_to_string(&mut buffer).unwrap();
        assert_eq!(buffer, code);
    }

    /// Test cmd_create rejects invalid paths
    #[test]
    fn test_cmd_create_validates_paths() {
        let temp_dir = TempDir::new().unwrap();

        // Test that creating file in non-existent directory requires create flag
        let nested_path = temp_dir.path().join("nested").join("file.rs");

        // This should work (creates parent directories)
        assert!(!nested_path.exists());
    }

    /// Test cmd_create returns correct result types
    #[test]
    fn test_cmd_create_return_types() {
        use crate::error::Result;

        // Function should return Result<()>
        fn check_signature() -> Result<()> {
            Ok(())
        }

        assert!(check_signature().is_ok());
    }
}