-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit e62ea0b
Showing
39 changed files
with
3,261 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
name: Rust | ||
|
||
on: | ||
push: | ||
branches: [ next, next ] | ||
pull_request: | ||
branches: [ next ] | ||
|
||
env: | ||
CARGO_TERM_COLOR: always | ||
|
||
jobs: | ||
build: | ||
|
||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- uses: actions/checkout@v2 | ||
|
||
- name: Build | ||
run: cargo build --features sqlite --verbose | ||
|
||
- name: Run tests with sqlite features | ||
run: cargo test --features sqlite --verbose | ||
|
||
- name: Run tests with mysql features | ||
run: cargo test --features mysql --verbose | ||
|
||
- name: Run tests with postgres features | ||
run: cargo test --features postgres --verbose |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
/target | ||
/Cargo.lock | ||
|
||
# Added by cargo | ||
# | ||
# already existing elements were commented out | ||
|
||
#/target | ||
#/Cargo.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"editor.formatOnSave": true, | ||
"editor.formatOnType": true, | ||
"[rust]": { | ||
"editor.defaultFormatter": "rust-lang.rust-analyzer", | ||
"editor.formatOnSave": true | ||
}, | ||
"rust-analyzer.linkedProjects": ["./Cargo.toml"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
[workspace] | ||
members = [".", "arel-macros", "./example"] | ||
|
||
[workspace.dependencies] | ||
log = "0.4" | ||
|
||
uuid = "1" | ||
serde = { version = "1.0", features = ["derive"] } | ||
serde_json = "1.0" | ||
bytes = { version = "1", features = ["serde"] } | ||
chrono = { version = "0.4", features = ["serde"] } | ||
|
||
# ============================================================================================= | ||
|
||
[package] | ||
name = "arel" | ||
version = "0.3.0" | ||
edition = "2021" | ||
license = "MIT OR Apache-2.0" | ||
description = "a sql orm base sqlx" | ||
authors = ["sanmu <578595193@qq.com>"] | ||
homepage = "https://github.com/rust-china/arel" | ||
categories = ["database"] | ||
keywords = ["async", "orm", "sqlite", "mysql", "postgres"] | ||
|
||
[features] | ||
default = ["with-json", "with-chrono"] | ||
sqlite = ["sqlx/sqlite"] | ||
mysql = ["sqlx/mysql"] | ||
postgres = ["sqlx/postgres"] | ||
runtime-tokio-native-tls = ["sqlx/runtime-tokio-native-tls"] | ||
runtime-tokio-rustls = ["sqlx/runtime-tokio-rustls"] | ||
runtime-tokio = ["sqlx/runtime-tokio"] | ||
runtime-async-std-native-tls = ["sqlx/runtime-async-std-native-tls"] | ||
runtime-async-std-rustls = ["sqlx/runtime-async-std-rustls"] | ||
runtime-async-std = ["sqlx/runtime-async-std"] | ||
tls-native-tls = ["sqlx/tls-native-tls"] | ||
tls-rustls = ["sqlx/tls-rustls"] | ||
|
||
with-json = ["serde_json", "bytes/serde", "chrono?/serde", "uuid?/serde", "sqlx/json"] | ||
with-chrono = ["chrono", "sqlx/chrono"] | ||
|
||
[package.metadata.docs.rs] | ||
features = ["sqlite"] | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
[dev-dependencies] | ||
arel = { path = ".", features = ["runtime-tokio", "tls-rustls"] } | ||
tokio = { version = "1", features = ["full"] } | ||
|
||
[dependencies] | ||
once_cell = "1.18" | ||
async-trait = "0.1" | ||
anyhow = "1.0" | ||
thiserror = "1.0" | ||
regex = "1.9" | ||
|
||
bytes = { version = "1", features = ["serde"] } | ||
serde = { version = "1.0", features = ["derive"] } | ||
serde_json = { version = "1", optional = true } | ||
chrono = { version = "0.4", features = ["serde"], optional = true } | ||
uuid = { version = "1", optional = true } | ||
sqlx = { version = "0.7" } | ||
|
||
# arel-macros = { version = "0.3.0" } | ||
arel-macros = { path = "./arel-macros" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
[package] | ||
name = "arel-macros" | ||
version = "0.3.0" | ||
edition = "2021" | ||
license = "MIT OR Apache-2.0" | ||
description = "arel macros" | ||
authors = ["sanmu <578595193@qq.com>"] | ||
|
||
[lib] | ||
proc-macro = true | ||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
syn = { version = "2", features = ["extra-traits", "full"] } | ||
proc-macro2 = { version = "1" } | ||
quote = { version = "1" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// fn _table_name() -> &'static str; | ||
pub(crate) fn impl_table_name(input: &crate::ItemInput) -> syn::Result<proc_macro2::TokenStream> { | ||
let mut ret_token_stream = proc_macro2::TokenStream::new(); | ||
if let Some((table_name, _)) = input.get_args_path_value(vec![], "table_name", None)? { | ||
ret_token_stream.extend(quote::quote!( | ||
fn _table_name() -> &'static str { | ||
#table_name | ||
} | ||
)); | ||
} | ||
if ret_token_stream.is_empty() { | ||
Err(syn::Error::new_spanned(&input.input, r#"Please set arel(table_name = "xxx")"#)) | ||
} else { | ||
Ok(ret_token_stream) | ||
} | ||
} | ||
// fn _primary_keys() -> Vec<&'static str> | ||
pub(crate) fn impl_primary_keys(input: &crate::ItemInput) -> syn::Result<proc_macro2::TokenStream> { | ||
let fields = input.struct_fields()?; | ||
let mut primary_keys: Vec<String> = vec![]; | ||
for field in fields.iter() { | ||
if let Some(_) = crate::ItemInput::get_field_path_value(field, vec!["arel"], "primary_key", None)? { | ||
if let Some(field_ident) = &field.ident { | ||
if let Some((rename, _)) = crate::ItemInput::get_field_path_value(field, vec!["arel"], "rename", None)? { | ||
primary_keys.push(rename); | ||
} else { | ||
primary_keys.push(field_ident.to_string().trim_start_matches("r#").to_string()); | ||
} | ||
} | ||
} | ||
} | ||
|
||
let mut ret_token_stream = proc_macro2::TokenStream::new(); | ||
if primary_keys.len() > 0 { | ||
ret_token_stream.extend(quote::quote!( | ||
fn _primary_keys() -> Vec<&'static str> { | ||
vec![#(#primary_keys),*] | ||
} | ||
)) | ||
} | ||
|
||
Ok(ret_token_stream) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
mod arel_trait; | ||
|
||
use proc_macro::TokenStream; | ||
use quote::ToTokens; | ||
use syn::parse::Parser; | ||
|
||
pub fn create_arel(args: TokenStream, input: TokenStream) -> TokenStream { | ||
let input = crate::ItemInput { | ||
args: if args.is_empty() { | ||
None | ||
} else { | ||
Some(syn::punctuated::Punctuated::<syn::Meta, syn::Token![,]>::parse_terminated.parse(args).unwrap()) | ||
}, | ||
input: syn::parse_macro_input!(input as syn::Item), | ||
}; | ||
|
||
match do_expand(&input) { | ||
Ok(token_stream) => token_stream.into(), | ||
Err(e) => { | ||
let mut ret_token_stream = e.to_compile_error(); | ||
ret_token_stream.extend(input.input.to_token_stream()); | ||
ret_token_stream.into() | ||
} | ||
} | ||
} | ||
|
||
fn do_expand(input: &crate::ItemInput) -> syn::Result<proc_macro2::TokenStream> { | ||
match &input.input { | ||
syn::Item::Struct(_) => (), | ||
_ => return Err(syn::Error::new_spanned(&input.input, "arel only allow use on struct type")), | ||
} | ||
|
||
let model_name_ident = input.ident()?; | ||
let mut model_fields = vec![]; | ||
for field in input.struct_fields()?.iter() { | ||
let mut new_field = field.clone(); | ||
new_field.attrs = vec![]; | ||
model_fields.push(new_field); | ||
} | ||
|
||
let arel_trait_impl_table_name = arel_trait::impl_table_name(input)?; | ||
let arel_trait_impl_primary_keys = arel_trait::impl_primary_keys(input)?; | ||
let impl_trait_sqlx_from_row = impl_trait_sqlx_from_row(input)?; | ||
|
||
let generics = input.generics()?; | ||
let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); | ||
|
||
let vis = input.vis()?; | ||
Ok(quote::quote!( | ||
#[derive(Clone, Debug, Default, PartialEq)] | ||
#vis struct #model_name_ident #generics { | ||
#(#model_fields),* | ||
} | ||
|
||
impl #impl_generics arel::SuperArel for #model_name_ident #type_generics #where_clause { | ||
#arel_trait_impl_table_name | ||
#arel_trait_impl_primary_keys | ||
} | ||
|
||
#impl_trait_sqlx_from_row | ||
)) | ||
} | ||
|
||
// impl<'r> arel::sqlx::FromRow<'r, arel::db::DatabaseRow> for User { | ||
// fn from_row(row: &'r arel::db::DatabaseRow) -> Result<Self, sqlx::Error> { | ||
// let mut model = Self::default(); | ||
// model.id = <i32 as arel::ArelAttributeFromRow>::from_row(row, "id")?; | ||
// model.name = <String as arel::ArelAttributeFromRow>::from_row(row, "name")?; | ||
// model.age = <Option<i32> as arel::ArelAttributeFromRow>::from_row(row, "age")?; | ||
// model.gender = <Gender as arel::ArelAttributeFromRow>::from_row(row, "gender")?; | ||
// model.r#type = <String as arel::ArelAttributeFromRow>::from_row(row, "type")?; | ||
// model.address = <Option<String> as arel::ArelAttributeFromRow>::from_row(row, "address")?; | ||
// model.expired_at = <Option<chrono::DateTime<chrono::FixedOffset>> as arel::ArelAttributeFromRow>::from_row(row, "expired_at")?; | ||
// Ok(model) | ||
// } | ||
// } | ||
fn impl_trait_sqlx_from_row(input: &crate::ItemInput) -> syn::Result<proc_macro2::TokenStream> { | ||
let struct_ident = input.ident()?; | ||
let fields = input.struct_fields()?; | ||
|
||
let mut build_assign_clauses = vec![]; | ||
for field in fields.iter() { | ||
let mut new_field = field.clone(); | ||
new_field.attrs = vec![]; | ||
|
||
let ident = &field.ident; | ||
let r#type = &field.ty; | ||
|
||
let field_name = { | ||
// arel(rename="x") | ||
if let Some((rename, _)) = crate::ItemInput::get_field_path_value(field, vec!["arel"], "rename", None)? { | ||
rename | ||
} else { | ||
match ident { | ||
Some(ident) => ident.to_string().trim_start_matches("r#").to_string(), | ||
_ => return Err(syn::Error::new_spanned(field, "Field name can not Blank!")), //不可达 | ||
} | ||
} | ||
}; | ||
build_assign_clauses.push(quote::quote!( | ||
model.#ident = <#r#type as arel::ArelAttributeFromRow>::from_row(&row, #field_name)?; | ||
)); | ||
} | ||
|
||
let mut generics = input.generics()?.clone(); | ||
generics.params.push(syn::parse_quote!('_r)); | ||
let (impl_generics, _, _) = generics.split_for_impl(); | ||
let (_, type_generics, where_clause) = input.generics()?.split_for_impl(); | ||
Ok(quote::quote!( | ||
impl #impl_generics arel::sqlx::FromRow<'_r, arel::db::DatabaseRow> for #struct_ident #type_generics #where_clause { | ||
fn from_row(row: &'_r arel::db::DatabaseRow) -> arel::sqlx::Result<Self, arel::sqlx::Error> { | ||
let mut model = Self::default(); | ||
#(#build_assign_clauses)* | ||
Ok(model) | ||
} | ||
} | ||
)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
pub struct DeriveInput { | ||
pub args: Option<syn::punctuated::Punctuated<syn::Meta, syn::Token![,]>>, | ||
/** | ||
* input: syn::parse_macro_input!(input as syn::DeriveInput) | ||
* */ | ||
pub input: syn::DeriveInput, | ||
} | ||
|
||
impl DeriveInput { | ||
pub fn fields(&self) -> syn::Result<&syn::punctuated::Punctuated<syn::Field, syn::Token![,]>> { | ||
if let syn::Data::Struct(syn::DataStruct { | ||
fields: syn::Fields::Named(syn::FieldsNamed { named, .. }), | ||
.. | ||
}) = &self.input.data | ||
{ | ||
Ok(named) | ||
} else { | ||
Err(syn::Error::new_spanned(&self.input, "Must call on struct")) | ||
} | ||
} | ||
} | ||
|
||
impl DeriveInput { | ||
pub fn get_field_path_value(field: &syn::Field, root_attr_paths: Vec<&str>, attr_path: &str, allowed_path_names: Option<Vec<&str>>) -> syn::Result<Option<(String, Option<syn::Lit>)>> { | ||
let metas = field.attrs.iter().map(|f| &f.meta).collect::<Vec<&syn::Meta>>(); | ||
Self::get_path_value_from_metas(metas, root_attr_path, attr_path, allowed_path_names) | ||
} | ||
pub fn get_path_value_from_metas(metas: Vec<&syn::Meta>, root_attr_paths: Vec<&str>, attr_path: &str, allowed_path_names: Option<Vec<&str>>) -> syn::Result<Option<(String, Option<syn::Lit>)>> { | ||
for meta in metas { | ||
match super::get_path_value_from_meta(meta, Some(root_attr_path), attr_path, allowed_path_names.clone())? { | ||
Some(v) => return Ok(Some(v)), | ||
None => continue, | ||
} | ||
} | ||
Ok(None) | ||
} | ||
} |
Oops, something went wrong.