ormer 0.2.2

A minimalist ORM framework that supports SQLite, PostgreSQL, MySQL, and SqlServer
Documentation
#![cfg(feature = "postgresql")]

mod _test_common;

#[derive(Debug, Clone, PartialEq, ormer::Model)]
#[table = "test_postgresql_arrays_1"]
struct PgArrayModel {
    #[primary]
    id: i32,
    ints: Vec<i32>,
    bigints: Vec<i64>,
    nullable_bigints: Vec<Option<i64>>,
}

#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(i32)]
enum PgArrayRole {
    Admin = 1,
    Ops = 2,
}

impl TryFrom<i32> for PgArrayRole {
    type Error = &'static str;

    fn try_from(value: i32) -> Result<Self, Self::Error> {
        match value {
            1 => Ok(Self::Admin),
            2 => Ok(Self::Ops),
            _ => Err("unknown role"),
        }
    }
}

#[derive(Debug, Clone, PartialEq, ormer::Model)]
#[table = "test_postgresql_arrays_2"]
struct PgEnumArrayModel {
    #[primary]
    id: i32,
    #[data_type(Vec<i32>)]
    roles: Vec<PgArrayRole>,
}

#[derive(Debug, Clone, PartialEq, ormer::Model)]
#[table = "test_postgresql_arrays_3"]
struct PgStringArrayModel {
    #[primary]
    id: i32,
    tags: Vec<String>,
}

#[tokio::test]
async fn test_postgresql_array_sql_and_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
    let config = _test_common::postgresql_config();

    let sql = ormer::generate_create_table_sql::<PgArrayModel>(config.0)?;
    assert!(sql.contains("ints INTEGER[] NOT NULL"), "{sql}");
    assert!(sql.contains("bigints BIGINT[] NOT NULL"), "{sql}");
    assert!(sql.contains("nullable_bigints BIGINT[] NOT NULL"), "{sql}");

    let db = _test_common::create_db_connection(&config).await?;
    let _ = db.drop_table::<PgArrayModel>().execute().await;

    db.create_table::<PgArrayModel>().execute().await?;
    db.validate_table::<PgArrayModel>().await?;

    let model = PgArrayModel {
        id: 1,
        ints: vec![1, 2, 3],
        bigints: vec![10, 20, 30],
        nullable_bigints: vec![Some(100), None, Some(300)],
    };

    db.insert(&model).execute().await?;

    let items = db.select::<PgArrayModel>().collect::<Vec<_>>().await?;
    assert_eq!(items, vec![model]);

    db.drop_table::<PgArrayModel>().execute().await?;
    Ok(())
}

#[tokio::test]
async fn test_postgresql_enum_array_data_type_roundtrip() -> Result<(), Box<dyn std::error::Error>>
{
    let config = _test_common::postgresql_config();

    let sql = ormer::generate_create_table_sql::<PgEnumArrayModel>(config.0)?;
    assert!(sql.contains("roles INTEGER[] NOT NULL"), "{sql}");

    let db = _test_common::create_db_connection(&config).await?;
    let _ = db.drop_table::<PgEnumArrayModel>().execute().await;

    db.create_table::<PgEnumArrayModel>().execute().await?;
    db.validate_table::<PgEnumArrayModel>().await?;

    let model = PgEnumArrayModel {
        id: 1,
        roles: vec![PgArrayRole::Admin, PgArrayRole::Ops],
    };

    db.insert(&model).execute().await?;

    let items = db.select::<PgEnumArrayModel>().collect::<Vec<_>>().await?;
    assert_eq!(items, vec![model]);

    db.drop_table::<PgEnumArrayModel>().execute().await?;
    Ok(())
}

#[tokio::test]
async fn test_postgresql_string_array_uses_text_array_value()
-> Result<(), Box<dyn std::error::Error>> {
    let config = _test_common::postgresql_config();

    let sql = ormer::generate_create_table_sql::<PgStringArrayModel>(config.0)?;
    assert!(sql.contains("tags TEXT[] NOT NULL"), "{sql}");

    let model = PgStringArrayModel {
        id: 1,
        tags: vec!["alpha".to_string(), "beta".to_string()],
    };

    let values = <PgStringArrayModel as ormer::Model>::field_values(&model);
    match &values[1] {
        ormer::Value::TextArray(tags) => assert_eq!(tags, &model.tags),
        other => panic!("expected TextArray value, got {:?}", other),
    }

    let db = _test_common::create_db_connection(&config).await?;
    let _ = db.drop_table::<PgStringArrayModel>().execute().await;

    db.create_table::<PgStringArrayModel>().execute().await?;
    db.validate_table::<PgStringArrayModel>().await?;

    db.insert(&model).execute().await?;

    let items = db
        .select::<PgStringArrayModel>()
        .collect::<Vec<_>>()
        .await?;
    assert_eq!(items, vec![model]);

    db.drop_table::<PgStringArrayModel>().execute().await?;
    Ok(())
}