vexy-json 1.5.13

A forgiving JSON parser that accepts non-standard JSON formats
Documentation
// this_file: tests/string_handling.rs

use vexy_json::{parse, Value};

/// String handling tests adapted for vexy_json's actual capabilities
/// NOTE: vexy_json supports single and double quoted strings but NOT backtick strings

#[test]
fn test_basic_string_types() {
    // Test single and double quoted strings (vexy_json's supported types)
    assert_eq!(parse("''").unwrap(), Value::String("".to_string()));
    assert_eq!(parse(r#""""#).unwrap(), Value::String("".to_string()));

    assert_eq!(parse("'a'").unwrap(), Value::String("a".to_string()));
    assert_eq!(parse(r#""a""#).unwrap(), Value::String("a".to_string()));

    assert_eq!(parse("'a b'").unwrap(), Value::String("a b".to_string()));
    assert_eq!(parse(r#""a b""#).unwrap(), Value::String("a b".to_string()));
}

#[test]
fn test_string_escape_sequences() {
    // Standard JSON escape sequences that vexy_json supports
    assert_eq!(parse(r#""\n""#).unwrap(), Value::String("\n".to_string()));
    assert_eq!(parse(r#""\t""#).unwrap(), Value::String("\t".to_string()));
    assert_eq!(parse(r#""\r""#).unwrap(), Value::String("\r".to_string()));
    assert_eq!(parse(r#""\"""#).unwrap(), Value::String("\"".to_string()));
    assert_eq!(parse(r#""\\""#).unwrap(), Value::String("\\".to_string()));
    assert_eq!(parse(r#""/""#).unwrap(), Value::String("/".to_string()));

    // Test escape sequences in single quoted strings
    assert_eq!(parse(r"'a\tb'").unwrap(), Value::String("a\tb".to_string()));
    assert_eq!(parse(r"'a\nb'").unwrap(), Value::String("a\nb".to_string()));
}

#[test]
fn test_quote_escaping() {
    // Test escaping quotes within their own quote type
    assert_eq!(parse("'a\\'b'").unwrap(), Value::String("a'b".to_string()));
    assert_eq!(
        parse("\"a\\\"b\"").unwrap(),
        Value::String("a\"b".to_string())
    );
}

#[test]
fn test_unicode_escapes() {
    // Test unicode escape sequences (if supported by vexy_json)
    let unicode_test = parse(r#""\u0061""#);
    if unicode_test.is_ok() {
        assert_eq!(unicode_test.unwrap(), Value::String("a".to_string()));
    } else {
        println!("vexy_json doesn't support unicode escapes - that's OK");
    }
}

#[test]
fn test_string_in_objects() {
    // Test supported string types as object values (no backticks)
    let result = parse(r#"{a: 'single', b: "double"}"#).unwrap();
    if let Value::Object(map) = result {
        assert_eq!(map.get("a"), Some(&Value::String("single".to_string())));
        assert_eq!(map.get("b"), Some(&Value::String("double".to_string())));
    } else {
        panic!("Expected object with string values");
    }
}

#[test]
fn test_string_in_arrays() {
    // Test supported string types in arrays (no backticks)
    let result = parse(r#"['single', "double"]"#).unwrap();
    if let Value::Array(arr) = result {
        assert_eq!(arr.len(), 2);
        assert_eq!(arr[0], Value::String("single".to_string()));
        assert_eq!(arr[1], Value::String("double".to_string()));
    } else {
        panic!("Expected array with string elements");
    }
}

#[test]
fn test_string_error_cases() {
    // Test unterminated strings - these should error
    assert!(
        parse(r#""x"#).is_err(),
        "Unterminated double quote should error"
    );
    assert!(
        parse("'x").is_err(),
        "Unterminated single quote should error"
    );

    // Test that backticks are not recognized as string delimiters
    assert!(
        parse("`test`").is_err(),
        "Backtick strings should not be supported"
    );
}

#[test]
fn test_newline_handling() {
    // Test how vexy_json handles newlines in strings
    let unescaped_newline_result = parse("\"\n\"");
    if unescaped_newline_result.is_ok() {
        println!("vexy_json allows unescaped newlines in double quotes");
    } else {
        println!("vexy_json errors on unescaped newlines - following JSON standard");
        assert!(unescaped_newline_result.is_err());
    }

    // Escaped newlines should always work
    assert_eq!(parse(r#""\n""#).unwrap(), Value::String("\n".to_string()));
}

#[test]
fn test_escape_edge_cases() {
    // Test unknown escape sequences behavior
    let unknown_escape_result = parse(r#""\w""#);
    if unknown_escape_result.is_ok() {
        if let Ok(Value::String(s)) = unknown_escape_result {
            // Document whatever behavior vexy_json has
            println!("vexy_json handles \\w as: {s:?}");
            assert!(
                s == "w" || s == "\\w" || s == "\\\\w",
                "Unknown escape behavior should be consistent"
            );
        }
    } else {
        println!("vexy_json errors on unknown escape sequences - that's valid behavior");
    }
}

#[test]
fn test_string_edge_cases() {
    // Test empty strings and whitespace
    assert_eq!(parse("''").unwrap(), Value::String("".to_string()));
    assert_eq!(parse(r#""""#).unwrap(), Value::String("".to_string()));

    // Test strings with just spaces
    assert_eq!(parse("' '").unwrap(), Value::String(" ".to_string()));
    assert_eq!(parse(r#"" ""#).unwrap(), Value::String(" ".to_string()));

    // Test strings with special characters
    assert_eq!(
        parse("'hello world'").unwrap(),
        Value::String("hello world".to_string())
    );
    assert_eq!(
        parse(r#""hello world""#).unwrap(),
        Value::String("hello world".to_string())
    );
}

#[test]
fn test_mixed_quotes_in_structures() {
    // Test complex structures with mixed quote types (supported ones only)
    let result = parse(
        r#"{
        single: 'value with "double" quotes',
        double: "value with 'single' quotes"
    }"#,
    )
    .unwrap();

    if let Value::Object(map) = result {
        assert_eq!(
            map.get("single"),
            Some(&Value::String("value with \"double\" quotes".to_string()))
        );
        assert_eq!(
            map.get("double"),
            Some(&Value::String("value with 'single' quotes".to_string()))
        );
    } else {
        panic!("Expected object with mixed quote strings");
    }
}

#[test]
fn test_backslash_behavior() {
    // Test how vexy_json handles backslashes in unknown escape sequences
    // This is adaptive testing - we test what vexy_json actually does
    let test_cases = vec![(r#""\q""#, "backslash + q"), (r#""\z""#, "backslash + z")];

    for (input, description) in test_cases {
        match parse(input) {
            Ok(Value::String(s)) => {
                println!("vexy_json handles {description} as: {s:?}");
                // Just verify it's consistent behavior, whatever it is
                assert!(!s.is_empty(), "String should not be empty");
            }
            Ok(other) => {
                panic!("Expected string or error for {description}, got: {other:?}");
            }
            Err(_) => {
                println!("vexy_json errors on {description} - that's valid behavior");
            }
        }
    }
}