rbs 4.8.0

Serialization framework for ORM
Documentation
use rbs::{Value};
use rbs::value::Table;

#[test]
fn test_table_basic() {
    let mut table = Table::new();
    table.add_column(Value::String("id".to_string()));
    table.add_column(Value::String("name".to_string()));
    table.add_row(vec![Value::I32(1), Value::String("Alice".to_string())]);
    table.add_row(vec![Value::I32(2), Value::String("Bob".to_string())]);

    let value = Value::Table(table.clone());

    assert!(value.is_table());
    let t = value.as_table().unwrap();
    assert_eq!(t.column_count(), 2);
    assert_eq!(t.row_count(), 2);
    assert_eq!(t.get(0, 0), Some(&Value::I32(1)));
    assert_eq!(t.get(0, 1), Some(&Value::String("Alice".to_string())));
    assert_eq!(t.get(1, 0), Some(&Value::I32(2)));
    assert_eq!(t.get(1, 1), Some(&Value::String("Bob".to_string())));
}

#[test]
fn test_table_serialize() {
    let mut table = Table::new();
    table.add_column(Value::String("id".to_string()));
    table.add_column(Value::String("name".to_string()));
    table.add_row(vec![Value::I32(1), Value::String("Alice".to_string())]);
    table.add_row(vec![Value::I32(2), Value::String("Bob".to_string())]);

    let value = Value::Table(table);
    let serialized = serde_json::to_string(&value).unwrap();

    // Should contain columns and rows
    assert!(!serialized.contains("__table__"));
    assert!(serialized.contains("columns"));
    assert!(serialized.contains("rows"));
    assert!(serialized.contains("\"id\""));
    assert!(serialized.contains("\"name\""));
    assert!(serialized.contains("[1,\"Alice\"]"));
    assert!(serialized.contains("[2,\"Bob\"]"));
}

#[test]
fn test_table_deserialize() {
    // No __table__ marker needed - just columns + rows
    let json = r#"{"columns":["id","name"],"rows":[[1,"Alice"],[2,"Bob"]]}"#;
    let value: Value = serde_json::from_str(json).unwrap();
    assert!(value.is_table());
    let table = value.as_table().unwrap();

    assert_eq!(table.columns.len(), 2);
    assert_eq!(table.rows.len(), 2);
    // JSON numbers deserialize as U64 by default
    assert_eq!(table.rows[0], vec![Value::U64(1), Value::String("Alice".to_string())]);
    assert_eq!(table.rows[1], vec![Value::U64(2), Value::String("Bob".to_string())]);
}

#[test]
fn test_table_roundtrip() {
    let mut original = Table::new();
    original.add_column(Value::String("id".to_string()));
    original.add_column(Value::String("name".to_string()));
    original.add_column(Value::String("status".to_string()));
    // Use U64 to match JSON deserialization behavior
    original.add_row(vec![Value::U64(1), Value::String("Alice".to_string()), Value::U64(1)]);
    original.add_row(vec![Value::U64(2), Value::String("Bob".to_string()), Value::U64(0)]);
    original.add_row(vec![Value::U64(3), Value::String("Charlie".to_string()), Value::U64(1)]);

    let value = Value::Table(original.clone());
    let json = serde_json::to_string(&value).unwrap();
    // Should not contain __table__ marker
    assert!(!json.contains("__table__"));
    let recovered: Value = serde_json::from_str(&json).unwrap();
    let recovered_table = recovered.as_table().unwrap();

    assert_eq!(original.columns, recovered_table.columns);
    assert_eq!(original.rows, recovered_table.rows);
}

#[test]
fn test_table_with_nulls() {
    let mut table = Table::new();
    table.add_column(Value::String("id".to_string()));
    table.add_column(Value::String("name".to_string()));
    table.add_row(vec![Value::I32(1), Value::Null]);
    table.add_row(vec![Value::Null, Value::String("Bob".to_string())]);

    let value = Value::Table(table);
    let json = serde_json::to_string(&value).unwrap();
    let recovered: Value = serde_json::from_str(&json).unwrap();
    let t = recovered.as_table().unwrap();

    assert_eq!(t.rows[0][1], Value::Null);
    assert_eq!(t.rows[1][0], Value::Null);
}

#[test]
fn test_table_len_and_is_empty() {
    let mut table = Table::new();
    let value = Value::Table(table.clone());
    assert!(value.is_empty());
    assert_eq!(value.len(), 0);

    table.add_column(Value::String("id".to_string()));
    table.add_row(vec![Value::I32(1)]);
    let value = Value::Table(table);
    assert!(!value.is_empty());
    assert_eq!(value.len(), 1);
}

#[test]
fn test_table_display() {
    let mut table = Table::new();
    table.add_column(Value::String("id".to_string()));
    table.add_column(Value::String("name".to_string()));
    table.add_row(vec![Value::I32(1), Value::String("Alice".to_string())]);

    let value = Value::Table(table);
    let display = format!("{}", value);

    // Should be compact JSON with columns and rows
    assert!(display.contains("columns"));
    assert!(display.contains("rows"));
    assert!(display.contains("id"));
    assert!(display.contains("name"));
}

#[test]
fn test_table_into_table() {
    let mut table = Table::new();
    table.add_column(Value::String("x".to_string()));
    table.add_row(vec![Value::I32(42)]);

    let value = Value::Table(table.clone());
    let recovered = value.into_table().unwrap();
    assert_eq!(recovered.columns, table.columns);
    assert_eq!(recovered.rows, table.rows);

    let not_table = Value::String("test".to_string());
    assert!(not_table.into_table().is_none());
}

#[test]
fn test_table_hash() {
    let mut t1 = Table::new();
    t1.add_column(Value::String("id".to_string()));
    t1.add_row(vec![Value::I32(1)]);

    let mut t2 = Table::new();
    t2.add_column(Value::String("id".to_string()));
    t2.add_row(vec![Value::I32(1)]);

    let mut t3 = Table::new();
    t3.add_column(Value::String("id".to_string()));
    t3.add_row(vec![Value::I32(2)]);

    let v1 = Value::Table(t1);
    let v2 = Value::Table(t2);
    let v3 = Value::Table(t3);

    assert_eq!(v1, v2);
    assert_ne!(v1, v3);
}

#[test]
fn test_table_with_complex_types() {
    let mut table = Table::new();
    table.add_column(Value::String("id".to_string()));
    table.add_column(Value::String("data".to_string()));
    table.add_row(vec![
        Value::I32(1),
        Value::Array(vec![Value::String("a".to_string()), Value::String("b".to_string())]),
    ]);

    let value = Value::Table(table);
    let json = serde_json::to_string(&value).unwrap();
    let recovered: Value = serde_json::from_str(&json).unwrap();
    let t = recovered.as_table().unwrap();

    if let Value::Array(arr) = &t.rows[0][1] {
        assert_eq!(arr.len(), 2);
        assert_eq!(arr[0], Value::String("a".to_string()));
    } else {
        panic!("Expected array");
    }
}