1 unstable release

new 0.1.11 Apr 7, 2025

#85 in #condition

Download history 67/week @ 2025-04-01

68 downloads per month
Used in 4 crates (3 directly)

MIT/Apache

4KB
69 lines

Great Art Stretches Taste.

Building Version Version

Features

  • Ergonomics : Ergonomics API design and Error design.
  • Transactional : Beautiful Transaction Abstraction, As not a Transaction.
  • Template : Write Your Own Sql Like Mybatis.
  • Asynchronous : Fully Async Based on Sqlx.
  • Compile-Time : Maximizing Compile-Time Processing, For API and Performance.

๐ŸŽ†0.1.10 will be a big version
After two months of hard work, the codebase of this library has grown from 26K to 37K, and version 0.1.10 is finally almost ready. The code is located in the following directories:

taitan-orm/brave_new
taitan-orm-macro
taitan-orm-parser
taitan-orm-trait/brave_new

The core functionality has been developed, and the current task is to clean up the old code by moving it to an old directory and relocating the code from the brave_new directory to the main default directory. Once this final task is completed, version 0.1.10 will be released.

1. api will have minor adjust, error will be re-designed
2. sqlite/mysql/postgres support will be feature, so there is no extra code generation that you do not need
3. attribute defined will use short words
4. most sql generation move to compile time rather than a runtime sql generator
5. new logic struct [AND OR NOT] to support logic condition
6. a new designd template parser

Quick Start

taitan-orm = { version = "0.1.9" }

use std::borrow::Cow;
use sqlx::types::time::PrimitiveDateTime;
use time::macros::datetime;
use taitan_orm::database::sqlite::SqliteDatabase;
use taitan_orm::database::sqlite::SqliteLocalConfig;
use taitan_orm::prelude::*;

#[derive(Schema, Clone, Debug)]
#[table_name = "user"]
#[unique_key = "name"]
#[index(name = "idx_hello", fields("age", "birthday"))]
pub struct User {
 #[primary_key]
 id: i32,
 name: String,
 age: Optional<i32>,
 birthday: Optional<PrimitiveDateTime>
}

#[tokio::main]
async fn main() -> taitan_orm::result::Result<()> {
 tracing_subscriber::fmt()
         .with_max_level(tracing::Level::TRACE)
         .init();

 // 0. setup database
 // refer to docs/setup.md for database setup
 let db = ...; 

 // 1. insert entity
 let entity = User {
  id: 1,
  name: "Allen".to_string(),
  age: Optional::Some(23),
  birthday: Optional::Some(datetime!(2019-01-01 0:00))
 };
 let result = db.insert(&entity).await?;
 assert_eq!(result, true);

 // 2. update
 let mutation = UserMutation {
  name: Optional::None,
  age: Optional::Some(24),
  birthday: Optional::None
 };
 let primary = UserPrimary { id: 1 };
 let result = db.update(&mutation, &primary).await?;
 assert_eq!(result, true);

 // 3. select
 let selection = UserSelectedEntity::full_fields();
 let entity: Option<UserSelectedEntity> 
         = db.select(&selection, &primary).await?;
 assert!(entity.is_some());

 // 4. select by unique
 let uk = UserNameUnique { name: "Allen".to_string() };
 let unique_entity : Option<UserSelectedEntity> 
         = db.select(&selection, &uk).await?;
 assert!(unique_entity.is_some());

 // 5. search by index
 let index = UserIndexIdxHello::AgeBirthday {
  age: LocationExpr::from("=", 24)?,
  birthday: LocationExpr::from("=", datetime!(2019-01-01 0:00))?
 };
 let index_entities: Vec<UserSelectedEntity> = db.search(&selection, &index, &None, &None).await?;
 assert_eq!(index_entities.len(), 1);

 // 6. search
 let selection = UserSelectedEntity::full_fields();
 let location = UserLocationExpr::id(">=", 1)?;
 let entities: Vec<UserSelectedEntity> = db.search(&selection, &location, &None, &None).await?;
 assert_eq!(entities.len(), 1);

 // 5. delete
 let result = db.delete(&primary).await?;
 assert_eq!(result, true);

 let entity: Option<UserSelectedEntity> = db.select(&selection, &primary).await?;
 assert!(entity.is_none());

 println!("crud success!");
 Ok(())
}
  • you can run the crud example in examples/crud directory.

Usage

Compile Time Generation

When derive(Schema), TaiTan-ORM will generate helper struct for you.

#[derive(Schema, Clone, Debug)]
#[table_name = "user"]
#[unique_key = "name"]
#[index(name = "idx_hello", fields("age", "birthday"))]
pub struct User {
 #[primary_key]
 id: i32,
 name: String,
 age: Optional<i32>,
 birthday: Optional<PrimitiveDateTime>
}

// struct for primary key 
pub struct UserPrimary {
 id: i32
}

// struct for update 
pub struct UserMutation {
 name: Optional<String>,
 age: Optional<i32>,
 birthday: Optional<PrimitiveDateTime>
}

// struct for unique key
pub struct UserNameUnique { 
 name: String 
}

// struct for index_hello, designed for index prefix matching
// age is allowed,
// age, birthday is allowed
// birthday is not allowed
pub enum UserIndexIdxHello {
    Age { 
        age: LocationExpr<i32> 
    },
    AgeBirthday{ 
        age: LocationExpr<i32>, 
        birthday: LocationExpr<PrimitiveDateTime> 
    }
}

// struct to generate where condition 
pub struct UserLocation {
 mode: LocationMode,
 id: Optional<LocationExpr<i32>>,
 name: Optional<LocationExpr<String>>,
 age: Optional<LocationExpr<i32>>,
}

// struct to select field and recv result from database 
pub struct UserSelectedEntity {
 id: Optional<i32>,
 name: Optional<String>,
 age: Optional<i32>,
}

Template

TaiTan-ORM: The Most Powerful ORM Template Engine You'll Ever Meet

1. Comprehensive Template Features with Jinja Syntax Support

Titan-ORM offers a full-featured template engine that supports the popular Jinja syntax, providing powerful templating capabilities.

2. Compile-Time Processing for Zero Runtime Overhead

In the vast majority of cases, compile-time processing is utilized, resulting in essentially zero overhead during runtime. This ensures optimal performance and efficiency.

3. A Revolutionary Optional Syntax

Introducing a new Optional syntax that helps you avoid cumbersome and unsightly template code, making your templates cleaner and more maintainable.

โš ๏ธ warning: before 0.2, syntax may change

syntax description
#{} compile time render syntax, may change
${} dynamic render syntax
%{} now used for optional, but it may confused with {% %}, so maybe change
{% %} Jinja template syntax, this will not change
@{} candidate for compile time render
/// This is the #{} syntax, this will be parsed at compile-time,
/// In run time, engine will get the sql: 
/// UPDATE `user` SET name = ? WHERE `id` = ?
/// There will be zero-overhead at run-time
#[derive(TemplateRecord, Debug)]
#[sql = "UPDATE `user` SET name = #{name} WHERE `id` = #{id}"]
pub struct UserUpdateTemplate {
    id: i32,
    name: String,
}

/// This is the ${} syntax, and will render the sql at run-time,
/// After render, engine get the sql: 
/// UPDATE `user` SET user_name = 2 WHERE `id` = 100
/// This is usually used for dynamic sql generation
#[derive(TemplateRecord, Debug)]
#[sql = "UPDATE `user` SET ${name} = 2 WHERE `id` = ${id}"]
pub struct UserUpdateTemplate {
 id: i32,      // suppose to be 100
 name: String, // suppose to be "user_name"
}

/// This is the %{} syntax, the most beautiful one, will be parsed at run time,
/// But Special for Optional variable
/// 1. When template is { name: "Allen", age: None },
///    After render, engine will get the sql: 
///    select `id`, `name`, `age` FROM `user` where `name` = ?
/// 2. When template is { name: "Allen", age: Some(33) },
///    After render, engine will get the sql: 
///    select `id`, `name`, `age` FROM `user` where age >= ? AND `name` = ?
///
/// when parser encounters "%{age}", 
/// it will treat the entire expression with connective 
/// "age >= %{age} AND" as an integrated unit
/// when Optional variable is None, the entire expression will be ignored
#[derive(TemplateRecord, Debug)]
#[sql = "select `id`, `name`, `age` FROM `user` where age >= %{age} AND `name` = #{name}"]
pub struct UserCustomTemplate {
 name: String,
 age: Optional<i32>,
}

  • you can run the template example in examples/template directory.

Transaction

Seamless transaction handling

async fn trx_insert_user(
    db: &mut SqliteDatabase,
    user1: &User,
    user2: &User,
) -> taitan_orm::result::Result<()> {
    let mut trx = db.transaction().await?; // create a transaction, trx
    trx.insert(user1).await?;              // same api as database
    trx.insert(user2).await?;              // rollback if there is any error
    trx.commit().await?;                   // commit it
    Ok(())                                 // when trx drop, if not commit, rollback 
}

Concepts

Documents

  1. Write API

Examples

At present, the documentation for this newly-born project is limited. You can refer to the examples project for more details.

example description
crud basic crud example
template template with paged example
transaction basic transaction example
search multi search features
axum_crud integrate with axum

Supported Database

MySql
Postgres
Sqlite
Please refer to ๐Ÿ”—Setup for details

ROADMAP

  • 0.1 API ๐Ÿ”ง
    โš ๏ธAPI may change, so just have a taste!
    What is being polished?

    1. write api: to support postgres insert returning syntax and batch insert/batch upsert
    1. search api: support index and more
    1. error
  • 0.2 Correctness: specification and code coverage and fuzz ๐Ÿ“Ž
    ๐Ÿ™Help is wanted, maybe a long-running mysql instance and a postgres instance
    now there is a rough coverage report: ๐Ÿชงreport

  • 0.3 Documentation: doc the usage and implementation ๐Ÿ“Ž
    ๐Ÿ–Š๏ธStarting from version 0.3, I will focus my efforts on documentation.

  • 0.4 Performance: benchmark and optimize ๐Ÿ“Ž
    ๐Ÿš€The ultimate speed originates from maximizing compile-time processing. But we need to exhibit it.

  • 1.0 Stable: stabilize the api, macro and error ๐Ÿ“Ž

LICENSE

Apache License

Dependencies

~82KB