langcodec-cli 0.11.0

A universal CLI tool for converting and inspecting localization files (Apple, Android, CSV, etc.)
Documentation
use std::fs;
use std::process::Command;

fn langcodec_cmd() -> Command {
    Command::new(assert_cmd::cargo::cargo_bin!("langcodec"))
}

#[test]
fn test_langcodec_comma_separated_include() {
    // Create a test file with multiple languages
    let test_content = r#"key,en,fr,zh-hans,de
hello,Hello,Bonjour,你好,Hallo
world,World,Monde,世界,Welt"#;

    fs::write("test_comma_include.csv", test_content).expect("Failed to create test file");

    let output = langcodec_cmd()
        .args([
            "convert",
            "-i",
            "test_comma_include.csv",
            "-o",
            "test_comma_include_filtered.langcodec",
            "--include-lang",
            "en,zh-hans",
        ])
        .output()
        .expect("Failed to execute command");

    assert!(
        output.status.success(),
        "Command failed: {}",
        String::from_utf8_lossy(&output.stderr)
    );

    // Check that the output file was created
    assert!(std::path::Path::new("test_comma_include_filtered.langcodec").exists());

    // Validate the output structure and check that only English and Chinese are included
    let content = fs::read_to_string("test_comma_include_filtered.langcodec")
        .expect("Failed to read output file");
    let resources: Result<Vec<langcodec::Resource>, _> = serde_json::from_str(&content);
    assert!(resources.is_ok(), "Output should be valid Resource array");

    let resources = resources.unwrap();
    assert_eq!(
        resources.len(),
        2,
        "Should have 2 resources (en and zh-hans)"
    );

    // Check that only English and Chinese are present
    let languages: Vec<&str> = resources
        .iter()
        .map(|r| r.metadata.language.as_str())
        .collect();
    assert!(languages.contains(&"en"), "English should be present");
    assert!(languages.contains(&"zh-hans"), "Chinese should be present");
    assert!(!languages.contains(&"fr"), "French should not be present");
    assert!(!languages.contains(&"de"), "German should not be present");

    // Clean up
    let _ = fs::remove_file("test_comma_include.csv");
    let _ = fs::remove_file("test_comma_include_filtered.langcodec");
}

#[test]
fn test_langcodec_comma_separated_exclude() {
    // Create a test file with multiple languages
    let test_content = r#"key,en,fr,zh-hans,de
hello,Hello,Bonjour,你好,Hallo
world,World,Monde,世界,Welt"#;

    fs::write("test_comma_exclude.csv", test_content).expect("Failed to create test file");

    let output = langcodec_cmd()
        .args([
            "convert",
            "-i",
            "test_comma_exclude.csv",
            "-o",
            "test_comma_exclude_filtered.langcodec",
            "--exclude-lang",
            "fr,de",
        ])
        .output()
        .expect("Failed to execute command");

    assert!(
        output.status.success(),
        "Command failed: {}",
        String::from_utf8_lossy(&output.stderr)
    );

    // Check that the output file was created
    assert!(std::path::Path::new("test_comma_exclude_filtered.langcodec").exists());

    // Validate the output structure and check that French and German are excluded
    let content = fs::read_to_string("test_comma_exclude_filtered.langcodec")
        .expect("Failed to read output file");
    let resources: Result<Vec<langcodec::Resource>, _> = serde_json::from_str(&content);
    assert!(resources.is_ok(), "Output should be valid Resource array");

    let resources = resources.unwrap();
    assert_eq!(
        resources.len(),
        2,
        "Should have 2 resources (en and zh-hans)"
    );

    // Check that French and German are excluded
    let languages: Vec<&str> = resources
        .iter()
        .map(|r| r.metadata.language.as_str())
        .collect();
    assert!(languages.contains(&"en"), "English should be present");
    assert!(languages.contains(&"zh-hans"), "Chinese should be present");
    assert!(!languages.contains(&"fr"), "French should be excluded");
    assert!(!languages.contains(&"de"), "German should be excluded");

    // Clean up
    let _ = fs::remove_file("test_comma_exclude.csv");
    let _ = fs::remove_file("test_comma_exclude_filtered.langcodec");
}

#[test]
fn test_langcodec_mixed_syntax() {
    // Test mixing comma-separated and multiple options
    let test_content = r#"key,en,fr,zh-hans,de,es
hello,Hello,Bonjour,你好,Hallo,Hola
world,World,Monde,世界,Welt,Mundo"#;

    fs::write("test_mixed_syntax.csv", test_content).expect("Failed to create test file");

    let output = langcodec_cmd()
        .args([
            "convert",
            "-i",
            "test_mixed_syntax.csv",
            "-o",
            "test_mixed_syntax_filtered.langcodec",
            "--include-lang",
            "en,zh-hans",
            "--include-lang",
            "es",
        ])
        .output()
        .expect("Failed to execute command");

    assert!(
        output.status.success(),
        "Command failed: {}",
        String::from_utf8_lossy(&output.stderr)
    );

    // Check that the output file was created
    assert!(std::path::Path::new("test_mixed_syntax_filtered.langcodec").exists());

    // Validate the output structure
    let content = fs::read_to_string("test_mixed_syntax_filtered.langcodec")
        .expect("Failed to read output file");
    let resources: Result<Vec<langcodec::Resource>, _> = serde_json::from_str(&content);
    assert!(resources.is_ok(), "Output should be valid Resource array");

    let resources = resources.unwrap();
    assert_eq!(
        resources.len(),
        3,
        "Should have 3 resources (en, zh-hans, es)"
    );

    // Check that all specified languages are present
    let languages: Vec<&str> = resources
        .iter()
        .map(|r| r.metadata.language.as_str())
        .collect();
    assert!(languages.contains(&"en"), "English should be present");
    assert!(languages.contains(&"zh-hans"), "Chinese should be present");
    assert!(languages.contains(&"es"), "Spanish should be present");
    assert!(!languages.contains(&"fr"), "French should not be present");
    assert!(!languages.contains(&"de"), "German should not be present");

    // Clean up
    let _ = fs::remove_file("test_mixed_syntax.csv");
    let _ = fs::remove_file("test_mixed_syntax_filtered.langcodec");
}

#[test]
fn test_langcodec_comma_with_spaces() {
    // Test that spaces around commas are handled correctly
    let test_content = r#"key,en,fr,zh-hans
hello,Hello,Bonjour,你好
world,World,Monde,世界"#;

    fs::write("test_comma_spaces.csv", test_content).expect("Failed to create test file");

    let output = langcodec_cmd()
        .args([
            "convert",
            "-i",
            "test_comma_spaces.csv",
            "-o",
            "test_comma_spaces_filtered.langcodec",
            "--include-lang",
            "en , zh-hans",
        ])
        .output()
        .expect("Failed to execute command");

    assert!(
        output.status.success(),
        "Command failed: {}",
        String::from_utf8_lossy(&output.stderr)
    );

    // Check that the output file was created
    assert!(std::path::Path::new("test_comma_spaces_filtered.langcodec").exists());

    // Validate the output structure
    let content = fs::read_to_string("test_comma_spaces_filtered.langcodec")
        .expect("Failed to read output file");
    let resources: Result<Vec<langcodec::Resource>, _> = serde_json::from_str(&content);
    assert!(resources.is_ok(), "Output should be valid Resource array");

    let resources = resources.unwrap();
    // Note: clap's value_delimiter doesn't trim whitespace, so "en " and " zh-hans" won't match
    // This test demonstrates the current behavior - spaces around commas are included in the language codes
    assert_eq!(
        resources.len(),
        0,
        "Should have 0 resources when language codes have spaces around commas"
    );

    // Clean up
    let _ = fs::remove_file("test_comma_spaces.csv");
    let _ = fs::remove_file("test_comma_spaces_filtered.langcodec");
}

#[test]
fn test_langcodec_complex_language_codes() {
    // Test with complex language codes that might contain hyphens
    let test_content = r#"key,en,en-US,zh-hans,zh-hant,pt-BR
hello,Hello,Hello,你好,你好,Olá
world,World,World,世界,世界,Mundo"#;

    fs::write("test_complex_langs.csv", test_content).expect("Failed to create test file");

    let output = langcodec_cmd()
        .args([
            "convert",
            "-i",
            "test_complex_langs.csv",
            "-o",
            "test_complex_langs_filtered.langcodec",
            "--include-lang",
            "en-US,zh-hans,pt-BR",
        ])
        .output()
        .expect("Failed to execute command");

    assert!(
        output.status.success(),
        "Command failed: {}",
        String::from_utf8_lossy(&output.stderr)
    );

    // Check that the output file was created
    assert!(std::path::Path::new("test_complex_langs_filtered.langcodec").exists());

    // Validate the output structure
    let content = fs::read_to_string("test_complex_langs_filtered.langcodec")
        .expect("Failed to read output file");
    let resources: Result<Vec<langcodec::Resource>, _> = serde_json::from_str(&content);
    assert!(resources.is_ok(), "Output should be valid Resource array");

    let resources = resources.unwrap();
    assert_eq!(
        resources.len(),
        3,
        "Should have 3 resources (en-US, zh-hans, pt-BR)"
    );

    // Check that all specified languages are present
    let languages: Vec<&str> = resources
        .iter()
        .map(|r| r.metadata.language.as_str())
        .collect();
    assert!(languages.contains(&"en-US"), "English US should be present");
    assert!(
        languages.contains(&"zh-hans"),
        "Chinese Simplified should be present"
    );
    assert!(
        languages.contains(&"pt-BR"),
        "Portuguese Brazil should be present"
    );
    assert!(!languages.contains(&"en"), "English should not be present");
    assert!(
        !languages.contains(&"zh-hant"),
        "Chinese Traditional should not be present"
    );

    // Clean up
    let _ = fs::remove_file("test_complex_langs.csv");
    let _ = fs::remove_file("test_complex_langs_filtered.langcodec");
}