FastORM 0.1.0

ORM based on sqlx
Documentation
use async_trait::async_trait;
use fastorm_trait::merge_any_arguments;
use fastorm_trait::GenericDaoMapper;
use fastorm_trait::{
    CmpOperator, Entity, Location, LocationExpr, Mutation, Primary, SelectedEntity, Selection,
};
use fastorm_macro::{Entity, Location, Mutation, Primary, SelectedEntity, Selection};
use path_absolutize::*;
use sqlx::any::AnyConnectOptions;
use sqlx::database::{Database, HasArguments};
use sqlx::mysql::{MySqlPool, MySqlRow};
use sqlx::sqlite::{SqliteConnectOptions, SqlitePool, SqliteRow};
use sqlx::Connection;
use sqlx::Error;
use sqlx::Pool;
use sqlx::{any::AnyArguments, any::AnyRow, Any, AnyPool};
use sqlx::{Arguments, FromRow, IntoArguments, Row};
use std::fs;
use std::marker::PhantomData;
use std::path::{Path, PathBuf};
use std::str::FromStr;

#[derive(Selection, Default, Clone)]
pub struct HelloSelection {
    id: Option<bool>,
    content: Option<bool>,
}

#[derive(Primary, Default, Clone)]
#[TableName = "article"]
pub struct HelloPrimary {
    id: i32,
}

#[derive(SelectedEntity, Debug, Clone, PartialEq, Eq)]
pub struct HelloSelectedEntity {
    id: Option<i32>,
    content: Option<String>,
}

#[derive(Entity, Clone, Debug)]
#[TableName = "article"]
pub struct HelloEntity {
    #[PrimaryKey]
    id: i32,
    content: String,
}

#[derive(Mutation, Clone, Debug)]
pub struct HelloMutation {
    content: String,
}

#[derive(Location, Clone, Debug)]
#[TableName = "article"]
pub struct HelloLocation {
    id: Option<LocationExpr<i32>>,
    content: Option<LocationExpr<String>>,
}

pub struct HelloMapperImpl {
    pool: AnyPool,
}

impl HelloMapperImpl {
    pub fn new(pool: AnyPool) -> Self {
        Self { pool }
    }
}

impl GenericDaoMapper for HelloMapperImpl {
    type P = HelloPrimary;
    type E = HelloEntity;
    type S = HelloSelection;
    type SE = HelloSelectedEntity;
    type L = HelloLocation;
    type M = HelloMutation;

    fn get_pool<'a>(&'a self) -> &'a AnyPool {
        return &self.pool;
    }
}

#[tokio::test]
pub async fn test_mapper() {
    let workspace = Path::new("./workspace");
    let workspace_absolute = workspace.absolutize().unwrap();
    fs::create_dir_all(&workspace_absolute).unwrap();
    let db_file_path = workspace_absolute.join("test.db");
    {
        let options = SqliteConnectOptions::new()
            .filename(db_file_path.clone())
            .create_if_missing(true);
        let _ = SqlitePool::connect_with(options).await.unwrap();
    }

    sqlx::any::install_default_drivers();
    let url = format!("sqlite:{}", db_file_path.to_str().unwrap());
    let any_options = AnyConnectOptions::from_str(&url).unwrap();
    let any_pool = AnyPool::connect_with(any_options).await.unwrap();
    let _ = sqlx::query("DROP TABLE IF EXISTS `article`")
        .execute(&any_pool)
        .await;
    let _ = sqlx::query(
        "CREATE TABLE IF NOT EXISTS `article`(`id` INT PRIMARY KEY, `content` VARCHAR(64))",
    )
    .execute(&any_pool)
    .await;

    let mapper = HelloMapperImpl::new(any_pool);

    let entity = HelloEntity {
        id: 23,
        content: "test article".to_string(),
    };
    let success = mapper.try_insert(entity).await.unwrap();
    assert_eq!(success, true);

    let selection = HelloSelection {
        id: None,
        content: Some(true),
    };
    let selected_fields = selection.get_selected_fields();
    assert_eq!(selected_fields, vec!["content".to_string()]);

    let primary = HelloPrimary { id: 23 };
    let result = mapper.try_select(primary.clone(), selection.clone()).await;
    dbg!(&result);
    assert!(result.is_ok());

    let props = HelloSelectedEntity {
        id: None,
        content: Some("test article".to_string()),
    };
    assert_eq!(result.unwrap(), Some(props));

    let result = mapper
        .try_update(HelloEntity {
            id: 23,
            content: "test article2".to_string(),
        })
        .await;
    assert_eq!(result.unwrap(), true);

    let result = mapper.try_select(primary.clone(), selection.clone()).await;
    let props = HelloSelectedEntity {
        id: None,
        content: Some("test article2".to_string()),
    };
    assert_eq!(result.unwrap(), Some(props));

    let upsert_entity = HelloEntity {
        id: 23,
        content: "upsert article".to_string(),
    };
    let result = mapper.try_upsert(upsert_entity).await;
    assert_eq!(result.unwrap(), true);

    let result = mapper.try_select(primary.clone(), selection.clone()).await;
    let props = HelloSelectedEntity {
        id: None,
        content: Some("upsert article".to_string()),
    };
    assert_eq!(result.unwrap(), Some(props));

    let result = mapper.try_delete(primary.clone()).await;
    assert_eq!(result.unwrap(), true);
    let result = mapper.try_select(primary.clone(), selection.clone()).await;
    assert_eq!(result.unwrap(), None);

    let entity1 = HelloEntity {
        id: 1,
        content: "test1".to_string(),
    };
    let entity2 = HelloEntity {
        id: 2,
        content: "test2".to_string(),
    };
    mapper.try_insert(entity1).await.unwrap();
    mapper.try_insert(entity2).await.unwrap();

    let location = HelloLocation {
        id: Some(LocationExpr {
            val: 1,
            cmp: CmpOperator::GreaterOrEq,
        }),
        content: None,
    };
    let entity_list_result = mapper.try_search(location.clone(), selection.clone()).await;
    assert!(entity_list_result.is_ok());
    let list = entity_list_result.unwrap();
    assert_eq!(list.len(), 2);

    let mutation = HelloMutation {
        content: "mutation".to_string(),
    };
    let result = mapper.try_change(location, mutation).await;
    assert_eq!(result.unwrap(), 2);
}