Initial Commit

This commit is contained in:
Chris Tsang 2021-05-07 03:35:46 +08:00
commit af4fb2ab7d
15 changed files with 342 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
target
Cargo.lock
*.sublime*
.vscode

31
Cargo.toml Normal file
View File

@ -0,0 +1,31 @@
[workspace]
members = [
".",
]
[package]
name = "sea-orm"
version = "0.1.0"
authors = [ "Chris Tsang <tyt2y7@gmail.com>" ]
edition = "2018"
description = "A database agnostic ORM for Rust"
license = "MIT OR Apache-2.0"
documentation = "https://docs.rs/sea-orm"
repository = "https://github.com/SeaQL/sea-orm"
categories = [ "database" ]
keywords = [ "orm", "database", "sql", "mysql", "postgres", "sqlite" ]
publish = false
[lib]
name = "sea_orm"
path = "src/lib.rs"
[dependencies]
async-trait = "^0.1"
async-std = { version = "^1.9", features = [ "attributes" ] }
futures = { version = "^0.3" }
sea-query = { version = "^0.10", features = [ "sqlx-mysql" ] }
# sea-schema = { path = "../sea-schema" }
serde = { version = "^1.0", features = [ "derive" ] }
sqlx = { version = "^0.5", features = [ "runtime-async-std-native-tls", "mysql", "any", "chrono", "time", "bigdecimal", "decimal", "uuid", "json" ] }
strum = { version = "^0.20", features = [ "derive" ] }

30
README.md Normal file
View File

@ -0,0 +1,30 @@
<div align="center">
<h1>SeaORM</h1>
<p>
<strong>An [adjective] ORM for Rust</strong>
</p>
<sub>Built with ❤️ by 🌊🦀🐠</sub>
</div>
# SeaORM - The Document ORM for Rust
## Features
1. Async
SeaORM is new to the party and rely on SQLx, so async support is not an afterthought.
2. Progressive
Built upon SeaQuery (a dynamic query builder), SeaORM allows you to build complex queries without 'fighting the ORM'.
3. Testable
Use mock connection to write unit tests for your logic.
4. API oriented
Quickly build search models that help you filter, sort and paginate data in APIs.

7
bakery.sql Normal file
View File

@ -0,0 +1,7 @@
DROP TABLE IF EXISTS `cake`;
CREATE TABLE `cake` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

1
src/driver/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod sqlx_mysql;

50
src/driver/sqlx_mysql.rs Normal file
View File

@ -0,0 +1,50 @@
use async_trait::async_trait;
use sqlx::{mysql::MySqlRow, MySqlPool};
sea_query::sea_query_driver_mysql!();
use sea_query_driver_mysql::bind_query;
use crate::{debug_print, executor::*, query::*};
pub struct SqlxMySqlExecutor {
pool: MySqlPool,
}
#[async_trait]
impl Executor for SqlxMySqlExecutor {
async fn query_one(&self, stmt: Statement) -> Result<QueryResult, ExecErr> {
debug_print!("{}, {:?}", sql, values);
let query = bind_query(sqlx::query(&stmt.sql), &stmt.values);
if let Ok(row) = query
.fetch_one(&mut self.pool.acquire().await.unwrap())
.await
{
Ok(row.into())
} else {
Err(ExecErr)
}
}
async fn query_all(&self, stmt: Statement) -> Result<Vec<QueryResult>, ExecErr> {
debug_print!("{}, {:?}", sql, values);
let query = bind_query(sqlx::query(&stmt.sql), &stmt.values);
if let Ok(rows) = query
.fetch_all(&mut self.pool.acquire().await.unwrap())
.await
{
Ok(rows.into_iter().map(|r| r.into()).collect())
} else {
Err(ExecErr)
}
}
}
impl From<MySqlRow> for QueryResult {
fn from(row: MySqlRow) -> QueryResult {
QueryResult {
row: QueryResultRow::SqlxMySql(row),
}
}
}

20
src/entity/base.rs Normal file
View File

@ -0,0 +1,20 @@
use super::{Column, Identity, Relation};
use sea_query::Iden;
use std::fmt::Debug;
use strum::IntoEnumIterator;
pub trait Entity: Iden + Default + Debug {
type Model;
type Column: Column + IntoEnumIterator;
type Relation: Relation + IntoEnumIterator;
fn table_name() -> Self;
fn primary_key() -> Identity;
fn auto_increment() -> bool {
true
}
}

6
src/entity/column.rs Normal file
View File

@ -0,0 +1,6 @@
pub use sea_query::ColumnType;
use sea_query::Iden;
pub trait Column: Iden {
fn col_type(&self) -> ColumnType;
}

22
src/entity/identity.rs Normal file
View File

@ -0,0 +1,22 @@
use sea_query::{Iden, IntoIden};
use std::rc::Rc;
#[derive(Debug, Clone)]
pub enum Identity {
Unary(Rc<dyn Iden>),
// Binary((Rc<dyn Iden>, Rc<dyn Iden>)),
// Ternary((Rc<dyn Iden>, Rc<dyn Iden>, Rc<dyn Iden>)),
}
pub trait IntoIdentity {
fn into_identity(self) -> Identity;
}
impl<T> IntoIdentity for T
where
T: IntoIden,
{
fn into_identity(self) -> Identity {
Identity::Unary(self.into_iden())
}
}

9
src/entity/mod.rs Normal file
View File

@ -0,0 +1,9 @@
mod base;
mod column;
mod identity;
mod relation;
pub use base::*;
pub use column::*;
pub use identity::*;
pub use relation::*;

91
src/entity/relation.rs Normal file
View File

@ -0,0 +1,91 @@
use super::{Identity, IntoIdentity};
use crate::Entity;
use sea_query::{Iden, IntoIden};
use std::rc::Rc;
#[derive(Debug)]
pub enum RelationType {
HasOne,
HasMany,
BelongsTo,
}
pub trait Relation {
fn rel_def() -> RelationDef;
}
pub struct RelationDef {
rel_type: RelationType,
from_tbl: Rc<dyn Iden>,
from_col: Identity,
to_col: Identity,
}
pub struct RelationBuilder {
rel_type: RelationType,
from_tbl: Rc<dyn Iden>,
from_col: Option<Identity>,
to_col: Option<Identity>,
}
impl RelationBuilder {
pub fn has_one<E: 'static>(entity: E) -> Self
where
E: Entity,
{
Self::new(RelationType::HasOne, entity)
}
pub fn has_many<E: 'static>(entity: E) -> Self
where
E: Entity,
{
Self::new(RelationType::HasMany, entity)
}
pub fn belongs_to<E: 'static>(entity: E) -> Self
where
E: Entity,
{
Self::new(RelationType::BelongsTo, entity)
}
fn new<E: 'static>(rel_type: RelationType, entity: E) -> Self
where
E: Entity,
{
Self {
rel_type,
from_tbl: entity.into_iden(),
from_col: None,
to_col: None,
}
}
pub fn from<T>(mut self, identifier: T) -> Self
where
T: IntoIdentity,
{
self.from_col = Some(identifier.into_identity());
self
}
pub fn to<T>(mut self, identifier: T) -> Self
where
T: IntoIdentity,
{
self.to_col = Some(identifier.into_identity());
self
}
}
impl From<RelationBuilder> for RelationDef {
fn from(b: RelationBuilder) -> Self {
RelationDef {
rel_type: b.rel_type,
from_tbl: b.from_tbl,
from_col: b.from_col.unwrap(),
to_col: b.to_col.unwrap(),
}
}
}

38
src/executor.rs Normal file
View File

@ -0,0 +1,38 @@
use crate::QueryResult;
use async_trait::async_trait;
use sea_query::Values;
use std::{error::Error, fmt};
pub struct Statement {
pub sql: String,
pub values: Values,
}
#[async_trait]
pub trait Executor {
async fn query_one(&self, stmt: Statement) -> Result<QueryResult, ExecErr>;
async fn query_all(&self, stmt: Statement) -> Result<Vec<QueryResult>, ExecErr>;
}
#[derive(Debug)]
pub struct ExecErr;
// ----- //
impl From<(String, Values)> for Statement {
fn from(stmt: (String, Values)) -> Statement {
Statement {
sql: stmt.0,
values: stmt.1,
}
}
}
impl Error for ExecErr {}
impl fmt::Display for ExecErr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}

10
src/lib.rs Normal file
View File

@ -0,0 +1,10 @@
mod driver;
mod entity;
mod executor;
mod query;
mod util;
pub use driver::*;
pub use entity::*;
pub use executor::*;
pub use query::*;

9
src/query.rs Normal file
View File

@ -0,0 +1,9 @@
use sqlx::mysql::MySqlRow;
pub struct QueryResult {
pub(crate) row: QueryResultRow,
}
pub(crate) enum QueryResultRow {
SqlxMySql(MySqlRow),
}

14
src/util.rs Normal file
View File

@ -0,0 +1,14 @@
#[macro_export]
#[cfg(feature = "debug-print")]
macro_rules! debug_print {
($( $args:expr ),*) => { println!( $( $args ),* ); }
}
#[macro_export]
// Non-debug version
#[cfg(not(feature = "debug-print"))]
macro_rules! debug_print {
($( $args:expr ),*) => {
true;
};
}