├── mybatis-core ├── src │ ├── oracle │ │ └── mod.rs │ ├── mssql │ │ ├── mod.rs │ │ └── sqlx_value.rs │ ├── mysql │ │ └── mod.rs │ ├── sqlite │ │ ├── mod.rs │ │ └── sqlx_value.rs │ ├── value.rs │ ├── postgres │ │ └── mod.rs │ ├── lib.rs │ ├── results │ │ └── mod.rs │ ├── db │ │ ├── mod.rs │ │ ├── bind_mssql.rs │ │ ├── bind_sqlite.rs │ │ └── bind_mysql.rs │ ├── convert.rs │ ├── types │ │ ├── bytes.rs │ │ ├── date_utc.rs │ │ ├── time_utc.rs │ │ ├── date_native.rs │ │ ├── time_native.rs │ │ ├── uuids.rs │ │ ├── decimal.rs │ │ ├── mod.rs │ │ ├── bools.rs │ │ ├── timestamp.rs │ │ ├── datetime_utc.rs │ │ └── json.rs │ └── decode.rs ├── .gitignore └── Cargo.toml ├── example ├── src │ ├── mod.rs │ ├── lib.rs │ ├── snowflake_test.rs │ ├── connection.rs │ ├── books.rs │ ├── plugin_page_test.rs │ ├── string_util_test.rs │ ├── wrapper_test.rs │ └── pets.rs └── Cargo.toml ├── mybatis-util ├── src │ ├── impled.rs │ ├── lib.rs │ ├── mod.rs │ ├── print_util.rs │ ├── error_util.rs │ ├── bencher.rs │ ├── time_util.rs │ ├── string_util.rs │ └── table_util.rs └── Cargo.toml ├── mybatis-macro ├── src │ ├── macros │ │ ├── mod.rs │ │ ├── mybatis_html_impl.rs │ │ ├── mybatis_sql_impl.rs │ │ └── py_sql_impl.rs │ ├── py_sql │ │ ├── string_node.rs │ │ ├── set_node.rs │ │ ├── where_node.rs │ │ ├── if_node.rs │ │ ├── print_node.rs │ │ ├── trim_node.rs │ │ ├── when_node.rs │ │ ├── choose_node.rs │ │ ├── foreach_node.rs │ │ ├── bind_node.rs │ │ ├── otherwise_node.rs │ │ └── error.rs │ ├── string_util.rs │ ├── html_loader.rs │ ├── util.rs │ └── element_from.rs ├── Cargo.toml └── README.md ├── .github ├── dependabot.yml ├── bors.toml └── workflows │ ├── rust.yml │ ├── greetings.yml │ ├── stale.yml │ └── manual.yml ├── mybatis-sql ├── src │ ├── mod.rs │ ├── lib.rs │ ├── from_bool.rs │ ├── ops_not.rs │ ├── from_sql.rs │ ├── bencher.rs │ ├── limit.rs │ ├── rule.rs │ ├── ops_bit_or.rs │ ├── ops_bit_and.rs │ ├── sql_replace.rs │ ├── string_util.rs │ ├── template.rs │ ├── error.rs │ └── ops_eq.rs └── Cargo.toml ├── Cargo.toml ├── mybatis ├── src │ ├── mod.rs │ ├── lib.rs │ ├── log.rs │ ├── snowflake.rs │ ├── intercept.rs │ └── logic_delete.rs ├── Cargo.toml └── README.md ├── CODE_OF_CONDUCT.md ├── .gitignore └── LICENSE /mybatis-core/src/oracle/mod.rs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /mybatis-core/src/mssql/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod sqlx_value; 2 | -------------------------------------------------------------------------------- /mybatis-core/src/mysql/mod.rs: -------------------------------------------------------------------------------- 1 | mod sqlx_value; 2 | -------------------------------------------------------------------------------- /example/src/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate summer_mybatis; -------------------------------------------------------------------------------- /mybatis-util/src/impled.rs: -------------------------------------------------------------------------------- 1 | /// mybatis_sql/py_sql impled 2 | #[macro_export] 3 | macro_rules! impled { 4 | () => {}; 5 | } 6 | -------------------------------------------------------------------------------- /mybatis-macro/src/macros/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod mybatis_html_impl; 2 | pub mod mybatis_plus_impl; 3 | pub mod mybatis_sql_impl; 4 | pub mod py_sql_impl; 5 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "cargo" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | time: "04:00" 8 | -------------------------------------------------------------------------------- /mybatis-sql/src/mod.rs: -------------------------------------------------------------------------------- 1 | use rbson::Bson; 2 | 3 | pub mod limit; 4 | pub mod rule; 5 | pub mod template; 6 | 7 | pub use limit::PageLimit; 8 | pub use template::TEMPLATE; 9 | -------------------------------------------------------------------------------- /.github/bors.toml: -------------------------------------------------------------------------------- 1 | status = [ 2 | "Check (nightly)", 3 | "Test (nightly)", 4 | "Rustfmt", 5 | "Clippy (nightly)", 6 | "Deploy (nightly)" 7 | ] 8 | delete_merged_branches = true 9 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "example", 4 | "mybatis-core", 5 | "mybatis-macro", 6 | "mybatis-sql", 7 | "mybatis-util", 8 | "mybatis", 9 | ] 10 | 11 | exclude = [".github/"] -------------------------------------------------------------------------------- /mybatis-util/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | pub mod bencher; 3 | #[macro_use] 4 | pub mod table_util; 5 | pub mod error_util; 6 | pub mod impled; 7 | pub mod print_util; 8 | pub mod string_util; 9 | pub mod time_util; 10 | -------------------------------------------------------------------------------- /mybatis-util/src/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | pub mod bencher; 3 | #[macro_use] 4 | pub mod table_util; 5 | pub mod error_util; 6 | pub mod print_util; 7 | pub mod string_util; 8 | pub mod time_util; 9 | pub mod impled; 10 | -------------------------------------------------------------------------------- /mybatis/src/mod.rs: -------------------------------------------------------------------------------- 1 | pub use mybatis_plugin::*; 2 | pub mod mybatis; 3 | pub mod wrapper; 4 | pub mod executor; 5 | pub mod intercept; 6 | pub mod log; 7 | pub mod logic_delete; 8 | pub mod object_id; 9 | pub mod page; 10 | pub mod snowflake; 11 | -------------------------------------------------------------------------------- /mybatis-macro/src/py_sql/string_node.rs: -------------------------------------------------------------------------------- 1 | use crate::py_sql::Name; 2 | 3 | #[derive(Clone, Debug)] 4 | pub struct StringNode { 5 | pub value: String, 6 | } 7 | 8 | impl Name for String { 9 | fn name() -> &'static str { 10 | "string" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /mybatis-macro/src/py_sql/set_node.rs: -------------------------------------------------------------------------------- 1 | use crate::py_sql::{Name, NodeType}; 2 | 3 | #[derive(Clone, Debug)] 4 | pub struct SetNode { 5 | pub childs: Vec, 6 | } 7 | 8 | impl Name for SetNode { 9 | fn name() -> &'static str { 10 | "set" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /mybatis-macro/src/py_sql/where_node.rs: -------------------------------------------------------------------------------- 1 | use crate::py_sql::{Name, NodeType}; 2 | 3 | #[derive(Clone, Debug)] 4 | pub struct WhereNode { 5 | pub childs: Vec, 6 | } 7 | 8 | impl Name for WhereNode { 9 | fn name() -> &'static str { 10 | "where" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /mybatis-macro/src/py_sql/if_node.rs: -------------------------------------------------------------------------------- 1 | use crate::py_sql::{Name, NodeType}; 2 | 3 | #[derive(Clone, Debug)] 4 | pub struct IfNode { 5 | pub childs: Vec, 6 | pub test: String, 7 | } 8 | 9 | impl Name for IfNode { 10 | fn name() -> &'static str { 11 | "if" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /mybatis-macro/src/py_sql/print_node.rs: -------------------------------------------------------------------------------- 1 | use crate::py_sql::{Name, NodeType}; 2 | 3 | #[derive(Clone, Debug)] 4 | pub struct PrintNode { 5 | pub value: String, 6 | pub format: String, 7 | } 8 | 9 | impl Name for PrintNode { 10 | fn name() -> &'static str { 11 | "println" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /mybatis-macro/src/py_sql/trim_node.rs: -------------------------------------------------------------------------------- 1 | use crate::py_sql::{Name, NodeType}; 2 | 3 | #[derive(Clone, Debug)] 4 | pub struct TrimNode { 5 | pub childs: Vec, 6 | pub trim: String, 7 | } 8 | 9 | impl Name for TrimNode { 10 | fn name() -> &'static str { 11 | "trim" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /mybatis-macro/src/py_sql/when_node.rs: -------------------------------------------------------------------------------- 1 | use crate::py_sql::{Name, NodeType}; 2 | 3 | #[derive(Clone, Debug)] 4 | pub struct WhenNode { 5 | pub childs: Vec, 6 | pub test: String, 7 | } 8 | 9 | impl Name for WhenNode { 10 | fn name() -> &'static str { 11 | "when" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /mybatis-core/src/sqlite/mod.rs: -------------------------------------------------------------------------------- 1 | //! **SQLite** database and connection types. 2 | // SQLite is a C library. All interactions require FFI which is unsafe. 3 | // All unsafe blocks should have comments pointing to SQLite docs and ensuring that we maintain 4 | // invariants. 5 | #![allow(unsafe_code)] 6 | 7 | mod sqlx_value; 8 | -------------------------------------------------------------------------------- /mybatis-core/src/value.rs: -------------------------------------------------------------------------------- 1 | use chrono::{Local, NaiveDate, NaiveDateTime}; 2 | 3 | pub trait DateTimeNow { 4 | fn now() -> NaiveDateTime; 5 | } 6 | 7 | impl DateTimeNow for NaiveDateTime { 8 | fn now() -> NaiveDateTime { 9 | let dt = Local::now(); 10 | dt.naive_local() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /example/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate mybatis; 3 | 4 | mod books; 5 | mod connection; 6 | mod pets; 7 | mod plugin_page_test; 8 | mod snowflake_test; 9 | mod string_util_test; 10 | mod wrapper_test; 11 | 12 | #[cfg(test)] 13 | mod test { 14 | #[test] 15 | fn test() { 16 | assert_eq!(4 + 4, 8); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /mybatis-macro/src/py_sql/choose_node.rs: -------------------------------------------------------------------------------- 1 | use crate::py_sql::{Name, NodeType}; 2 | 3 | #[derive(Clone, Debug)] 4 | pub struct ChooseNode { 5 | pub when_nodes: Vec, 6 | pub otherwise_node: Option>, 7 | } 8 | 9 | impl Name for ChooseNode { 10 | fn name() -> &'static str { 11 | "choose" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /mybatis-macro/src/py_sql/foreach_node.rs: -------------------------------------------------------------------------------- 1 | use crate::py_sql::{Name, NodeType}; 2 | 3 | #[derive(Clone, Debug)] 4 | pub struct ForEachNode { 5 | pub childs: Vec, 6 | pub collection: String, 7 | pub index: String, 8 | pub item: String, 9 | } 10 | 11 | impl Name for ForEachNode { 12 | fn name() -> &'static str { 13 | "for" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /mybatis-core/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | The summer mybatis project adheres to the [Rust Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct). This describes the minimum behavior expected from all contributors. 4 | 5 | ## Enforcement 6 | 7 | Instances of violations of the Code of Conduct can be reported by contacting the project team at [team@summeros.org](mailto:team@summeros.org). 8 | -------------------------------------------------------------------------------- /mybatis-util/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mybatis-util" 3 | version = "2.0.0" 4 | edition = "2021" 5 | rust-version = "1.59.0" 6 | authors = ["James Zow "] 7 | license = "Apache-2.0" 8 | description = "mybatis util" 9 | 10 | [dependencies] 11 | mybatis-core = {version = "2.0.0", path = "../mybatis-core"} 12 | rbson = { version = "2.0.4", features = ["uuid-0_8", "chrono-0_4"] } -------------------------------------------------------------------------------- /mybatis-macro/src/py_sql/bind_node.rs: -------------------------------------------------------------------------------- 1 | use crate::py_sql::{DefName, Name}; 2 | 3 | #[derive(Clone, Debug)] 4 | pub struct BindNode { 5 | pub name: String, 6 | pub value: String, 7 | } 8 | 9 | impl DefName for BindNode { 10 | fn def_name() -> &'static str { 11 | "let" 12 | } 13 | } 14 | 15 | impl Name for BindNode { 16 | fn name() -> &'static str { 17 | "bind" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /mybatis-macro/src/py_sql/otherwise_node.rs: -------------------------------------------------------------------------------- 1 | use crate::py_sql::{DefName, Name, NodeType}; 2 | 3 | #[derive(Clone, Debug)] 4 | pub struct OtherwiseNode { 5 | pub childs: Vec, 6 | } 7 | 8 | impl Name for OtherwiseNode { 9 | fn name() -> &'static str { 10 | "otherwise" 11 | } 12 | } 13 | 14 | impl DefName for OtherwiseNode { 15 | fn def_name() -> &'static str { 16 | "_" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /example/src/snowflake_test.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use mybatis::snowflake::new_snowflake_id; 4 | 5 | #[test] 6 | fn test_new_block_id() { 7 | println!("{}", new_snowflake_id()); 8 | println!("{}", new_snowflake_id()); 9 | } 10 | 11 | //cargo test --release --package mybatis --test snowflake_test test::test_bench_new_block_id --no-fail-fast -- --exact -Z unstable-options --show-output 12 | #[test] 13 | fn test_bench_new_block_id() {} 14 | } 15 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Build 20 | run: | 21 | cd mybatis 22 | cargo build --verbose 23 | - name: Run tests 24 | run: | 25 | cd mybatis 26 | cargo test --verbose 27 | -------------------------------------------------------------------------------- /example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example" 3 | version = "0.1.0" 4 | authors = ["James Zow "] 5 | edition = "2021" 6 | publish = false 7 | 8 | [dependencies] 9 | serde = { version = "1", features = ["derive"] } 10 | rbson = "2.0" 11 | tokio = { version = "1.18.2", features = ["full"] } 12 | mybatis = { version = "2.0.4", path = "../mybatis", features = ["debug_mode"]} 13 | mybatis-util = {version = "2.0.0", path = "../mybatis-util"} 14 | mybatis-core = {version = "2.0.1", path = "../mybatis-core"} -------------------------------------------------------------------------------- /.github/workflows/greetings.yml: -------------------------------------------------------------------------------- 1 | name: Greetings 2 | 3 | on: [pull_request_target, issues] 4 | 5 | jobs: 6 | greeting: 7 | runs-on: ubuntu-latest 8 | permissions: 9 | issues: write 10 | pull-requests: write 11 | steps: 12 | - uses: actions/first-interaction@v1 13 | with: 14 | repo-token: ${{ secrets.GITHUB_TOKEN }} 15 | issue-message: 'Message that will be displayed on users first issue' 16 | pr-message: 'Message that will be displayed on users first pull request' 17 | -------------------------------------------------------------------------------- /mybatis-core/src/postgres/mod.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | mod sqlx_value; 4 | 5 | #[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)] 6 | pub struct PgInterval { 7 | pub months: i32, 8 | pub days: i32, 9 | pub microseconds: i64, 10 | } 11 | 12 | impl From for PgInterval { 13 | fn from(arg: sqlx_core::postgres::types::PgInterval) -> Self { 14 | Self { 15 | months: arg.months, 16 | days: arg.days, 17 | microseconds: arg.microseconds, 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # idea 6 | /.idea/ 7 | /.idea 8 | .idea/ 9 | '.idea' 10 | # vscode 11 | /.vscode/ 12 | .vscode/ 13 | /.vscode 14 | '.vscode' 15 | /.github 16 | 17 | .DS_Store 18 | .travis.yml 19 | 20 | 21 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 22 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 23 | Cargo.lock 24 | 25 | # These are backup files generated by rustfmt 26 | **/*.rs.bk 27 | 28 | 29 | #log 30 | *.log 31 | 32 | 33 | -------------------------------------------------------------------------------- /mybatis-util/src/print_util.rs: -------------------------------------------------------------------------------- 1 | pub fn print_rust_mybatis() { 2 | let mut info = String::new(); 3 | info.push_str(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); 4 | info.push_str(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Summer MyBatis started >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); 5 | info.push_str(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Summer Rust Organization >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); 6 | info.push_str(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); 7 | println!("{}", info); 8 | } 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Summer Open Source Team 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /mybatis-sql/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mybatis-sql" 3 | version = "2.0.1" 4 | edition = "2021" 5 | rust-version = "1.59.0" 6 | authors = ["James Zow "] 7 | license = "Apache-2.0" 8 | description = "mybatis sql" 9 | 10 | [features] 11 | #default is fast_mode 12 | default = [] 13 | debug_mode = [] 14 | 15 | [dependencies] 16 | once_cell = "1.9.0" 17 | mybatis-core = { version = "2.0.0", path = "../mybatis-core" } 18 | mybatis-macro = { version = "2.0.2", path = "../mybatis-macro" } 19 | 20 | #serde 21 | serde = { version = "1", features = ["derive"] } 22 | rbson = "2.0.4" 23 | dashmap = { version = "6" } 24 | base64 = "0.21" 25 | async-trait = "0.1" -------------------------------------------------------------------------------- /mybatis-sql/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod limit; 2 | pub mod rule; 3 | pub mod template; 4 | 5 | pub use limit::PageLimit; 6 | pub use template::TEMPLATE; 7 | 8 | pub mod error; 9 | pub mod string_util; 10 | #[macro_use] 11 | pub mod bencher; 12 | pub mod ops; 13 | pub mod ops_cmp; 14 | pub mod ops_eq; 15 | pub mod ops_index; 16 | 17 | pub mod from_bool; 18 | pub mod from_sql; 19 | pub mod ops_add; 20 | pub mod ops_bit_and; 21 | pub mod ops_bit_or; 22 | pub mod ops_div; 23 | pub mod ops_mul; 24 | pub mod ops_not; 25 | pub mod ops_rem; 26 | pub mod ops_sub; 27 | pub mod ops_xor; 28 | 29 | #[macro_use] 30 | pub mod sql_replace; 31 | 32 | pub use mybatis_macro::{expr, html, py}; 33 | -------------------------------------------------------------------------------- /example/src/connection.rs: -------------------------------------------------------------------------------- 1 | use mybatis::mybatis::Mybatis; 2 | use mybatis::plus::Mapping; 3 | 4 | #[cfg(test)] 5 | mod tests { 6 | use super::*; 7 | 8 | #[tokio::test] 9 | async fn test_database() { 10 | // 连接数据库,自动判断驱动类型"mysql://*","postgres://*","sqlite://*","mssql://*"加载驱动 11 | // let rb = Mybatis::new(); 12 | // rb.link("mysql://root:passw0rd@localhost:3306/COVID-19").await.unwrap(); 13 | // use crate::core::db::DBPoolOptions; 14 | // let mut opt =DBPoolOptions::new(); 15 | // opt.max_connections=100; 16 | // rb.link_opt("mysql://root:123456@localhost:3306/test",&opt).await.unwrap(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /mybatis-util/src/error_util.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use mybatis_core::Error; 4 | 5 | pub trait ToResult { 6 | fn to_result(&self, fail_method: F) -> Result<&T, Error> 7 | where 8 | F: Fn() -> String; 9 | } 10 | 11 | impl ToResult for Option<&T> { 12 | fn to_result(&self, fail_method: F) -> Result<&T, Error> 13 | where 14 | F: Fn() -> String, 15 | { 16 | if self.is_none() { 17 | return Err(Error::from(fail_method())); 18 | } 19 | return Ok(self.unwrap()); 20 | } 21 | } 22 | 23 | #[test] 24 | fn test_to_result() { 25 | let i = 1; 26 | let v = Option::Some(&i); 27 | let r = v.to_result(|| String::new()); 28 | } 29 | -------------------------------------------------------------------------------- /mybatis-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Core of mybatis::core, the rust SQL toolkit. Not intended to be used directly. 2 | 3 | // #![warn(missing_docs)] 4 | #![allow(unused_imports)] 5 | #![allow(unused_assignments)] 6 | 7 | #[macro_use] 8 | extern crate lazy_static; 9 | #[macro_use] 10 | extern crate serde_json; 11 | pub use error::{Error, Result}; 12 | 13 | #[cfg(feature = "mssql")] 14 | mod mssql; 15 | /// database 16 | #[cfg(feature = "mysql")] 17 | mod mysql; 18 | #[cfg(feature = "postgres")] 19 | mod postgres; 20 | #[cfg(feature = "sqlite")] 21 | mod sqlite; 22 | #[macro_use] 23 | pub mod error; 24 | #[macro_use] 25 | pub mod convert; 26 | pub mod db; 27 | pub mod decode; 28 | pub mod results; 29 | pub mod types; 30 | pub mod value; 31 | pub use types::*; 32 | -------------------------------------------------------------------------------- /mybatis-sql/src/from_bool.rs: -------------------------------------------------------------------------------- 1 | use crate::ops::{AsProxy, From, Value}; 2 | 3 | impl From for bool { 4 | fn op_from(arg: bool) -> Self { 5 | arg 6 | } 7 | } 8 | impl From<&bool> for bool { 9 | fn op_from(arg: &bool) -> Self { 10 | *arg 11 | } 12 | } 13 | impl From<&&bool> for bool { 14 | fn op_from(arg: &&bool) -> Self { 15 | **arg 16 | } 17 | } 18 | 19 | impl From<&Value> for bool { 20 | fn op_from(arg: &Value) -> Self { 21 | arg.bool() 22 | } 23 | } 24 | 25 | impl From<&&Value> for bool { 26 | fn op_from(arg: &&Value) -> Self { 27 | arg.bool() 28 | } 29 | } 30 | 31 | impl From for bool { 32 | fn op_from(arg: Value) -> Self { 33 | arg.bool() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /mybatis-sql/src/ops_not.rs: -------------------------------------------------------------------------------- 1 | use crate::ops::Not; 2 | 3 | use crate::ops::Value; 4 | 5 | impl Not for Value { 6 | type Output = bool; 7 | 8 | fn op_not(self) -> Self::Output { 9 | match self { 10 | Value::Boolean(b) => !b, 11 | _ => true, 12 | } 13 | } 14 | } 15 | 16 | impl Not for &Value { 17 | type Output = bool; 18 | fn op_not(self) -> Self::Output { 19 | match self { 20 | Value::Boolean(b) => !*b, 21 | _ => true, 22 | } 23 | } 24 | } 25 | 26 | impl Not for &mut Value { 27 | type Output = bool; 28 | fn op_not(self) -> Self::Output { 29 | match self { 30 | Value::Boolean(b) => !*b, 31 | _ => true, 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | # This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time. 2 | # 3 | # You can adjust the behavior by modifying this file. 4 | # For more information, see: 5 | # https://github.com/actions/stale 6 | name: Mark stale issues and pull requests 7 | 8 | on: 9 | schedule: 10 | - cron: '33 4 * * *' 11 | 12 | jobs: 13 | stale: 14 | 15 | runs-on: ubuntu-latest 16 | permissions: 17 | issues: write 18 | pull-requests: write 19 | 20 | steps: 21 | - uses: actions/stale@v5 22 | with: 23 | repo-token: ${{ secrets.GITHUB_TOKEN }} 24 | stale-issue-message: 'Stale issue message' 25 | stale-pr-message: 'Stale pull request message' 26 | stale-issue-label: 'no-issue-activity' 27 | stale-pr-label: 'no-pr-activity' 28 | -------------------------------------------------------------------------------- /mybatis/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | pub extern crate mybatis_macro; 3 | #[macro_use] 4 | pub extern crate mybatis_sql; 5 | 6 | // mapping macros 7 | pub use mybatis_core as core; 8 | 9 | pub use mybatis_core::*; 10 | pub use mybatis_sql::ops::*; 11 | pub use mybatis_sql::{expr, html, push_index, py, sql_index}; 12 | 13 | pub use mybatis_macro::mybatis_plus; 14 | pub use mybatis_macro::MybatisPlus; 15 | 16 | pub use mybatis_macro::{mybatis_html, mybatis_sql, py_sql}; 17 | 18 | pub use mybatis_core::{convert::StmtConvert, db::DriverType, error::Error, error::Result}; 19 | 20 | pub use crate::mybatis::AsSqlTag; 21 | 22 | #[macro_use] 23 | pub mod mybatis; 24 | 25 | pub mod executor; 26 | pub mod intercept; 27 | pub mod log; 28 | pub mod logic_delete; 29 | pub mod object_id; 30 | pub mod page; 31 | pub mod plus; 32 | pub mod snowflake; 33 | pub mod wrapper; 34 | -------------------------------------------------------------------------------- /mybatis-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mybatis-macro" 3 | version = "2.0.3" 4 | description = "mybatis macro driver" 5 | authors = ["James Zow "] 6 | edition = "2021" 7 | license = "Apache-2.0" 8 | rust-version = "1.59.0" 9 | repository = "https://github.com/summer-rust/summer-mybatis/tree/master/mybatis-macro" 10 | homepage = "https://github.com/summer-rust/summer-mybatis/blob/master/mybatis-macro/README.md" 11 | documentation = "https://github.com/summer-rust/summer-mybatis/blob/master/mybatis-macro/README.md" 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [features] 16 | default = [] 17 | debug_mode = [] 18 | [lib] 19 | proc-macro = true 20 | [dependencies] 21 | proc-macro2 = "1.0" 22 | quote = "1.0" 23 | syn = { version = "1.0", features = ["full"] } 24 | html_parser = "0.6.2" 25 | ### 26 | base64 = "0.21.5" 27 | url = "2.2.2" -------------------------------------------------------------------------------- /example/src/books.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[mybatis_plus] 4 | #[derive(Debug, Serialize, Deserialize)] 5 | pub struct Books { 6 | pub id: Option, 7 | pub name: Option, 8 | pub types: Option, 9 | } 10 | 11 | #[cfg(test)] 12 | mod tests { 13 | use super::*; 14 | use mybatis::mybatis::Mybatis; 15 | use mybatis::plus::Mapping; 16 | use mybatis::snowflake::SNOWFLAKE; 17 | 18 | #[tokio::test] 19 | async fn save_books() { 20 | let mybatis = Mybatis::new(); 21 | 22 | mybatis 23 | .link("mysql://root:passw0rd@localhost:3306/test") 24 | .await 25 | .unwrap(); 26 | 27 | let id = SNOWFLAKE.generate(); 28 | let cat = Books { 29 | id: Some(id.to_string()), 30 | name: Some("《Daughter of the sea》".to_string()), 31 | types: Some("Fairy Tales".to_string()), 32 | }; 33 | 34 | mybatis.save(&cat, &[]).await; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /example/src/plugin_page_test.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use mybatis::page::{MyBatisReplacePagePlugin, PagePlugin, PageRequest}; 4 | use mybatis_core::db::DriverType; 5 | 6 | #[test] 7 | fn test_page() { 8 | let pack_page = MyBatisReplacePagePlugin {}; 9 | let sql = "select 10 | b.name, 11 | count (*) as total 12 | from 13 | (select * from demands where deleted_at is null) a 14 | left join 15 | (select * from demand_kinds where deleted_at is null) b 16 | on 17 | a.demand_kind_id = b.id 18 | group by 19 | b.name 20 | order by 21 | total desc"; 22 | let (count, select) = pack_page 23 | .make_page_sql( 24 | &DriverType::Postgres, 25 | &sql, 26 | &vec![], 27 | &PageRequest::new(1, 10), 28 | ) 29 | .unwrap(); 30 | println!("{}", count); 31 | println!("/////////////////"); 32 | println!("{}", select); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /mybatis-sql/src/from_sql.rs: -------------------------------------------------------------------------------- 1 | use crate::ops::{AsSql, Value}; 2 | 3 | impl AsSql for Value { 4 | fn as_sql(&self) -> String { 5 | match self { 6 | Value::String(s) => s.to_owned(), 7 | _ => self.to_string(), 8 | } 9 | } 10 | } 11 | 12 | impl AsSql for &Value { 13 | fn as_sql(&self) -> String { 14 | match self { 15 | Value::String(s) => s.to_owned(), 16 | _ => self.to_string(), 17 | } 18 | } 19 | } 20 | 21 | impl AsSql for &&Value { 22 | fn as_sql(&self) -> String { 23 | match self { 24 | Value::String(s) => s.to_owned(), 25 | _ => self.to_string(), 26 | } 27 | } 28 | } 29 | 30 | macro_rules! to_sql { 31 | ([$($ty:ty)*]) => { 32 | $(impl AsSql for $ty{ 33 | fn as_sql(&self) -> String { 34 | self.to_string() 35 | } 36 | })* 37 | }; 38 | } 39 | 40 | to_sql!([String & String && String]); 41 | to_sql!([&str && str]); 42 | to_sql!([i8 i16 i32 i64 isize]); 43 | to_sql!([u8 u16 u32 u64 usize]); 44 | to_sql!([f32 f64]); 45 | -------------------------------------------------------------------------------- /mybatis-sql/src/bencher.rs: -------------------------------------------------------------------------------- 1 | pub trait QPS { 2 | fn qps(&self, total: u64); 3 | fn time(&self, total: u64); 4 | fn cost(&self); 5 | } 6 | 7 | impl QPS for std::time::Instant { 8 | fn qps(&self, total: u64) { 9 | let time = self.elapsed(); 10 | println!( 11 | "use QPS: {} QPS/s", 12 | (total as u128 * 1000000000 as u128 / time.as_nanos() as u128) 13 | ); 14 | } 15 | 16 | fn time(&self, total: u64) { 17 | let time = self.elapsed(); 18 | println!( 19 | "use Time: {:?} ,each:{} ns/op", 20 | &time, 21 | time.as_nanos() / (total as u128) 22 | ); 23 | } 24 | 25 | fn cost(&self) { 26 | let time = self.elapsed(); 27 | println!("cost:{:?}", time); 28 | } 29 | } 30 | 31 | #[macro_export] 32 | macro_rules! bench { 33 | ($total:expr,$body:block) => {{ 34 | use mybatis_sql::bencher::QPS; 35 | let now = std::time::Instant::now(); 36 | for _ in 0..$total { 37 | $body; 38 | } 39 | now.time($total); 40 | now.qps($total); 41 | }}; 42 | } 43 | -------------------------------------------------------------------------------- /mybatis-util/src/bencher.rs: -------------------------------------------------------------------------------- 1 | pub trait QPS { 2 | fn qps(&self, total: u64); 3 | fn time(&self, total: u64); 4 | fn cost(&self); 5 | } 6 | 7 | impl QPS for std::time::Instant { 8 | fn qps(&self, total: u64) { 9 | let time = self.elapsed(); 10 | println!( 11 | "use QPS: {} QPS/s", 12 | (total as u128 * 1000000000 as u128 / time.as_nanos() as u128) 13 | ); 14 | } 15 | 16 | fn time(&self, total: u64) { 17 | let time = self.elapsed(); 18 | println!( 19 | "use Time: {:?} ,each:{} ns/op", 20 | &time, 21 | time.as_nanos() / (total as u128) 22 | ); 23 | } 24 | 25 | fn cost(&self) { 26 | let time = self.elapsed(); 27 | println!("cost:{:?}", time); 28 | } 29 | } 30 | 31 | #[macro_export] 32 | macro_rules! bench { 33 | ($total:expr,$body:block) => {{ 34 | use mybatis_util::bencher::QPS; 35 | let now = std::time::Instant::now(); 36 | for _ in 0..$total { 37 | $body; 38 | } 39 | now.time($total); 40 | now.qps($total); 41 | }}; 42 | } 43 | -------------------------------------------------------------------------------- /.github/workflows/manual.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow that is manually triggered 2 | 3 | name: Manual workflow 4 | 5 | # Controls when the action will run. Workflow runs when manually triggered using the UI 6 | # or API. 7 | on: 8 | workflow_dispatch: 9 | # Inputs the workflow accepts. 10 | inputs: 11 | name: 12 | # Friendly description to be shown in the UI instead of 'name' 13 | description: 'Person to greet' 14 | # Default value if no value is explicitly provided 15 | default: 'World' 16 | # Input has to be provided for the workflow to run 17 | required: true 18 | 19 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 20 | jobs: 21 | # This workflow contains a single job called "greet" 22 | greet: 23 | # The type of runner that the job will run on 24 | runs-on: ubuntu-latest 25 | 26 | # Steps represent a sequence of tasks that will be executed as part of the job 27 | steps: 28 | # Runs a single command using the runners shell 29 | - name: Send greeting 30 | run: echo "Hello ${{ github.event.inputs.name }}" 31 | -------------------------------------------------------------------------------- /mybatis-core/src/results/mod.rs: -------------------------------------------------------------------------------- 1 | use sqlx_core::error::BoxDynError; 2 | 3 | use crate::convert::ResultCodec; 4 | 5 | /// convert sqlx-Result to mybatis-core Result 6 | impl ResultCodec for Result { 7 | fn into_result(self) -> crate::Result { 8 | match self { 9 | Ok(t) => { 10 | return Ok(t); 11 | } 12 | Err(e) => { 13 | return Err(crate::Error::from(e.to_string())); 14 | } 15 | } 16 | } 17 | } 18 | 19 | /// convert sqlx-Result to mybatis-core Result 20 | impl ResultCodec for Result { 21 | fn into_result(self) -> crate::Result { 22 | match self { 23 | Ok(t) => { 24 | return Ok(t); 25 | } 26 | Err(e) => { 27 | return Err(crate::Error::from(e.to_string())); 28 | } 29 | } 30 | } 31 | } 32 | 33 | /// convert sqlx-Result to mybatis-core Result 34 | impl ResultCodec for Result { 35 | fn into_result(self) -> crate::Result { 36 | match self { 37 | Ok(t) => { 38 | return Ok(t); 39 | } 40 | Err(e) => { 41 | return Err(crate::Error::from(e.to_string())); 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /mybatis-util/src/time_util.rs: -------------------------------------------------------------------------------- 1 | use std::time::{Duration, Instant, SystemTime}; 2 | 3 | pub fn count_time_qps(tag: &str, total: u128, start: Instant) { 4 | print_qps(tag, total, start); 5 | print_each_time(tag, total, start); 6 | } 7 | 8 | ///count qps 9 | pub fn print_qps(tag: &str, total: u128, start: Instant) { 10 | let time = start.elapsed().as_nanos(); 11 | println!( 12 | "[count_qps] {} use qps: {} QPS/s", 13 | tag, 14 | (total * 1000000000 as u128 / time) 15 | ); 16 | } 17 | 18 | ///计算每个操作耗时ns纳秒 19 | pub fn print_each_time(tag: &str, total: u128, start: Instant) { 20 | let time = start.elapsed(); 21 | println!( 22 | "[count_each_time] {} use Time: {},each:{} ns/op", 23 | tag, 24 | duration_to_string(time), 25 | time.as_nanos() / total as u128 26 | ); 27 | } 28 | 29 | /// count wait time 30 | pub fn print_time(tag: &str, start: Instant) { 31 | let time = start.elapsed(); 32 | println!( 33 | "[count_wait_time] {} use Time: {} ", 34 | tag, 35 | duration_to_string(time) 36 | ); 37 | } 38 | 39 | /// duration_to_string 40 | fn duration_to_string(wait: Duration) -> String { 41 | if wait.gt(&Duration::from_millis(1)) { 42 | return format!("{}ms", wait.as_millis()); 43 | } else if wait.gt(&Duration::from_secs(1)) { 44 | return format!("{}s", wait.as_secs() as u128); 45 | } else { 46 | return format!("{}ns", wait.as_nanos()); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /mybatis-sql/src/limit.rs: -------------------------------------------------------------------------------- 1 | use crate::template::TEMPLATE; 2 | use mybatis_core::db::DriverType; 3 | use mybatis_core::Error; 4 | use mybatis_core::Result; 5 | 6 | pub trait PageLimit { 7 | /// return sql 8 | fn page_limit_sql(&self, offset: u64, size: u64) -> Result; 9 | } 10 | 11 | impl PageLimit for DriverType { 12 | fn page_limit_sql(&self, offset: u64, size: u64) -> Result { 13 | return match self { 14 | DriverType::Mysql => Ok(format!(" {} {},{}", TEMPLATE.limit.value, offset, size)), 15 | DriverType::Postgres => Ok(format!( 16 | " {} {} {} {}", 17 | TEMPLATE.limit.value, size, TEMPLATE.offset.value, offset 18 | )), 19 | DriverType::Sqlite => Ok(format!( 20 | " {} {} {} {}", 21 | TEMPLATE.limit.value, size, TEMPLATE.offset.value, offset 22 | )), 23 | DriverType::Mssql => { 24 | //sqlserver 25 | Ok(format!( 26 | " {} {} {} {} {}", 27 | TEMPLATE.offset.value, 28 | offset, 29 | TEMPLATE.rows_fetch_next.value, 30 | size, 31 | TEMPLATE.rows_only.value 32 | )) 33 | } 34 | DriverType::None => Err(Error::from(format!( 35 | "[mybatis] not support now for DriverType:{:?}", 36 | DriverType::None 37 | ))), 38 | }; 39 | } 40 | } 41 | 42 | #[test] 43 | pub fn test_create_limit() { 44 | let mysql_limit = DriverType::Mysql.page_limit_sql(1, 20).unwrap(); 45 | println!("{}", mysql_limit); 46 | let pg_limit = DriverType::Postgres.page_limit_sql(1, 20).unwrap(); 47 | println!("{}", pg_limit); 48 | let sqlite_limit = DriverType::Sqlite.page_limit_sql(1, 20).unwrap(); 49 | println!("{}", sqlite_limit); 50 | } 51 | -------------------------------------------------------------------------------- /mybatis-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mybatis-core" 3 | version = "2.0.1" 4 | description = "mybatis core by sqlx" 5 | license = "Apache-2.0" 6 | edition = "2021" 7 | authors = [ 8 | "Jmaes Zow " 9 | ] 10 | 11 | [features] 12 | default = ["all-database", "runtime-tokio-rustls"] 13 | debug_mode = [] 14 | all-database = ["sqlx-core/all-databases", "mysql", "postgres", "sqlite", "mssql", "bit-vec"] 15 | mysql = ["sqlx-core/mysql"] 16 | postgres = ["sqlx-core/postgres", "bit-vec"] 17 | sqlite = ["sqlx-core/sqlite"] 18 | mssql = ["sqlx-core/mssql"] 19 | 20 | runtime-tokio-rustls = ["sqlx-core/runtime-tokio-rustls"] 21 | runtime-tokio-native-tls = ["sqlx-core/runtime-tokio-native-tls"] 22 | runtime-actix-rustls = ["sqlx-core/runtime-actix-rustls"] 23 | runtime-actix-native-tls = ["sqlx-core/runtime-actix-native-tls"] 24 | runtime-async-std-rustls = ["sqlx-core/runtime-async-std-rustls"] 25 | runtime-async-std-native-tls = ["sqlx-core/runtime-async-std-native-tls"] 26 | 27 | format_bson = [] 28 | 29 | [dependencies] 30 | serde = { version = "1", features = ["derive"] } 31 | serde_json = { version = "1", features = ["raw_value","preserve_order"] } 32 | rbson = { version = "2.0.4", features = ["uuid-0_8", "chrono-0_4"] } 33 | chrono = { version = "0.4", default-features = false, features = ["clock", "serde"] } 34 | log = { version = "0.4", default-features = false } 35 | lazy_static = "1.4" 36 | ipnetwork = { version = "0.20", features = ["serde"] } 37 | sqlx-core = { version = "0.5", features = ["offline","chrono", "time", "bigdecimal", "decimal","json", "uuid", "bit-vec"], optional = true } 38 | uuid = { version = "0.8", features = ["serde", "v4"] } 39 | time = { version = "0.2.16", features = ["serde"] } 40 | #only pg 41 | bit-vec = { version = "0.6", features = ["serde"], optional = true } 42 | bigdecimal_ = { version = "0.2.0", package = "bigdecimal", features = ["serde"] } 43 | base64 = "0.21.5" 44 | hex = "0.4" -------------------------------------------------------------------------------- /example/src/string_util_test.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use mybatis_util::string_util::{find_convert_string, un_packing_string}; 4 | 5 | #[test] 6 | fn test_find() { 7 | let sql = "update user set name=#{name}, password=#{password} ,sex=#{sex}, phone=#{phone}, delete_flag=#{flag}, #{name} #{ 1 + "; 8 | let finds = find_convert_string(sql); 9 | println!("{:?}", finds); 10 | assert_eq!(finds.len(), 5); 11 | let mut index = 0; 12 | for (k, _) in &finds { 13 | if index == 0 { 14 | assert_eq!(k, "name"); 15 | } 16 | if index == 1 { 17 | assert_eq!(k, "password"); 18 | } 19 | if index == 2 { 20 | assert_eq!(k, "sex"); 21 | } 22 | if index == 3 { 23 | assert_eq!(k, "phone"); 24 | } 25 | if index == 4 { 26 | assert_eq!(k, "flag"); 27 | } 28 | index += 1; 29 | } 30 | println!("{:?}", finds); 31 | } 32 | 33 | #[test] 34 | fn test_find_fail() { 35 | let sql = "select #{column #{ }"; 36 | let finds = find_convert_string(sql); 37 | println!("{:?}", finds); 38 | } 39 | 40 | #[test] 41 | fn test_un_pack_string() { 42 | assert_eq!(un_packing_string(""), ""); 43 | assert_eq!(un_packing_string("''"), ""); 44 | assert_eq!(un_packing_string("``"), ""); 45 | assert_eq!(un_packing_string("\"\""), ""); 46 | assert_eq!(un_packing_string("`a`"), "a"); 47 | assert_eq!(un_packing_string("\""), "\""); 48 | } 49 | 50 | ///cargo test --release --package mybatis --test string_util_test test::bench_find --no-fail-fast -- --exact -Z unstable-options --show-output 51 | #[test] 52 | fn bench_find() { 53 | let sql = "update user set name=#{name}, password=#{password} ,sex=#{sex}, phone=#{phone}, delete_flag=#{flag}, #{name}"; 54 | find_convert_string(sql); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /mybatis-sql/src/rule.rs: -------------------------------------------------------------------------------- 1 | use crate::template::TEMPLATE; 2 | use mybatis_core::db::DriverType; 3 | 4 | pub trait SqlRule { 5 | fn make_where(&self, where_sql: &str) -> String { 6 | let sql = where_sql.trim_start(); 7 | if sql.is_empty() { 8 | return String::new(); 9 | } 10 | if sql.starts_with(TEMPLATE.order_by.right_space) 11 | || sql.starts_with(TEMPLATE.group_by.right_space) 12 | || sql.starts_with(TEMPLATE.limit.right_space) 13 | { 14 | sql.to_string() 15 | } else { 16 | format!( 17 | " {} {} ", 18 | TEMPLATE.r#where.value, 19 | sql.trim_start_matches(TEMPLATE.r#where.right_space) 20 | .trim_start_matches(TEMPLATE.and.right_space) 21 | .trim_start_matches(TEMPLATE.or.right_space) 22 | ) 23 | } 24 | } 25 | 26 | fn make_left_insert_where(&self, insert_sql: &str, where_sql: &str) -> String { 27 | let sql = where_sql 28 | .trim() 29 | .trim_start_matches(TEMPLATE.r#where.right_space) 30 | .trim_start_matches(TEMPLATE.and.right_space); 31 | if sql.is_empty() { 32 | return insert_sql.to_string(); 33 | } 34 | if sql.starts_with(TEMPLATE.order_by.right_space) 35 | || sql.starts_with(TEMPLATE.group_by.right_space) 36 | || sql.starts_with(TEMPLATE.limit.right_space) 37 | { 38 | format!( 39 | " {} {} {}", 40 | TEMPLATE.r#where.value, 41 | insert_sql.trim().trim_end_matches(TEMPLATE.and.left_space), 42 | sql 43 | ) 44 | } else { 45 | format!( 46 | " {} {} {} {}", 47 | TEMPLATE.r#where.value, 48 | insert_sql.trim().trim_end_matches(TEMPLATE.and.left_space), 49 | TEMPLATE.and.value, 50 | sql 51 | ) 52 | } 53 | } 54 | 55 | fn testcc(&self) -> String; 56 | } 57 | 58 | impl SqlRule for DriverType { 59 | fn testcc(&self) -> String { 60 | String::from("555") 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /mybatis-sql/src/ops_bit_or.rs: -------------------------------------------------------------------------------- 1 | use crate::ops::BitOr; 2 | use crate::ops::Value; 3 | 4 | impl BitOr for Value { 5 | type Output = bool; 6 | 7 | fn op_bitor(self, rhs: Self) -> Self::Output { 8 | self.as_bool().unwrap_or(false) | rhs.as_bool().unwrap_or(false) 9 | } 10 | } 11 | 12 | impl BitOr for bool { 13 | type Output = bool; 14 | 15 | fn op_bitor(self, rhs: Value) -> Self::Output { 16 | self | rhs.as_bool().unwrap_or(false) 17 | } 18 | } 19 | 20 | //ref 21 | impl BitOr for &Value { 22 | type Output = bool; 23 | 24 | fn op_bitor(self, rhs: Value) -> Self::Output { 25 | self.as_bool().unwrap_or(false) | rhs.as_bool().unwrap_or(false) 26 | } 27 | } 28 | impl BitOr<&Value> for &Value { 29 | type Output = bool; 30 | 31 | fn op_bitor(self, rhs: &Value) -> Self::Output { 32 | self.as_bool().unwrap_or(false) | rhs.as_bool().unwrap_or(false) 33 | } 34 | } 35 | impl BitOr<&&Value> for &Value { 36 | type Output = bool; 37 | 38 | fn op_bitor(self, rhs: &&Value) -> Self::Output { 39 | self.as_bool().unwrap_or(false) | rhs.as_bool().unwrap_or(false) 40 | } 41 | } 42 | 43 | impl BitOr for &Value { 44 | type Output = bool; 45 | 46 | fn op_bitor(self, rhs: bool) -> Self::Output { 47 | self.as_bool().unwrap_or(false) | rhs 48 | } 49 | } 50 | 51 | //rhs ref 52 | impl BitOr<&Value> for Value { 53 | type Output = bool; 54 | 55 | fn op_bitor(self, rhs: &Value) -> Self::Output { 56 | self.as_bool().unwrap_or(false) | rhs.as_bool().unwrap_or(false) 57 | } 58 | } 59 | 60 | impl BitOr<&Value> for bool { 61 | type Output = bool; 62 | 63 | fn op_bitor(self, rhs: &Value) -> Self::Output { 64 | self | rhs.as_bool().unwrap_or(false) 65 | } 66 | } 67 | 68 | impl BitOr<&&Value> for Value { 69 | type Output = bool; 70 | 71 | fn op_bitor(self, rhs: &&Value) -> Self::Output { 72 | self.as_bool().unwrap_or(false) | rhs.as_bool().unwrap_or(false) 73 | } 74 | } 75 | 76 | impl BitOr<&&Value> for bool { 77 | type Output = bool; 78 | 79 | fn op_bitor(self, rhs: &&Value) -> Self::Output { 80 | self | rhs.as_bool().unwrap_or(false) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /mybatis-macro/README.md: -------------------------------------------------------------------------------- 1 | # Mybatis Macro 2 | 3 | This module contains some macros and attributes built into mybatis, 4 | The source code of all macros is in this module. 5 | 6 | ## Getting Started 7 | 8 | ```rust 9 | mybatis-macro = "2.0.3" 10 | ``` 11 | 12 | ## Macro description 13 | 14 | ### 1. Macro drive MybatisPlus 15 | 16 | This macro is mainly used to automatically map the conditional packaging of some built-in operation tables. 17 | You can realize the basic operations of querying, deleting, adding and modifying data tables without spelling SQL statements 18 | 19 | ```rust 20 | #[derive(MybatisPlus)] 21 | pub struct Book { 22 | pub id: Option, 23 | pub name: Option, 24 | pub types: Option 25 | } 26 | ``` 27 | 28 | ### 2. Macro attribute mybatis_plus 29 | 30 | The function of this macro is the same as that of MybatisPlus macro, except that this macro is used for attribute. 31 | 32 | ```rust 33 | #[mybatis_plus] 34 | pub struct Book { 35 | pub id: Option, 36 | pub name: Option, 37 | pub types: Option 38 | } 39 | 40 | #[mybatis_plus(table_name:"book")] 41 | pub struct Book { 42 | pub id: Option, 43 | pub name: Option, 44 | pub types: Option 45 | } 46 | ``` 47 | 48 | ### 3. Macro attribute mybatis_sql 49 | This macro is mainly used to automatically create SQL statements for you. 50 | You can write SQL statements in the macro corresponding to a method implementation. 51 | 52 | ```rust 53 | #[mybatis_sql("select * from book where id = ?")] 54 | async fn select(mybatis:&Mybatis, id: &str) -> Book {} 55 | ``` 56 | 57 | ### 4. Macro attribute py_sql 58 | This macro is mainly used to process pysql, and its function is similar to that of mybatis_sql macro 59 | 60 | ```rust 61 | #[py_sql("select * from book where name = "《test》"")] 62 | async fn py_select(name: &str) -> Book {} 63 | ``` 64 | 65 | ### 5. Macro attribute mybatis_html 66 | This macro is mainly used to parse the SQL syntax structure in HTML. 67 | The bottom layer is to parse the HTML file in the specified directory through HTML parse 68 | 69 | ```rust 70 | #[mybatis_html("example/example.html")] 71 | async fn html_parse_mybatis(mybatis: &Mybatis, name: &str) -> Book {} 72 | ``` 73 | -------------------------------------------------------------------------------- /mybatis-macro/src/py_sql/error.rs: -------------------------------------------------------------------------------- 1 | //! Errorand Result types. 2 | use std::error::Error as StdError; 3 | use std::fmt::{self, Debug, Display}; 4 | use std::io; 5 | 6 | pub type Result = std::result::Result; 7 | 8 | /// A generic error that represents all the ways a method can fail inside of rexpr::core. 9 | #[derive(Debug)] 10 | #[non_exhaustive] 11 | pub enum Error { 12 | /// Default Error 13 | E(String), 14 | } 15 | 16 | impl Display for Error { 17 | // IntellijRust does not understand that [non_exhaustive] applies only for downstream crates 18 | // noinspection RsMatchCheck 19 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 20 | match self { 21 | Error::E(error) => write!(f, "{}", error), 22 | } 23 | } 24 | } 25 | 26 | impl StdError for Error {} 27 | 28 | impl From for Error { 29 | #[inline] 30 | fn from(err: io::Error) -> Self { 31 | Error::from(err.to_string()) 32 | } 33 | } 34 | 35 | impl From<&str> for Error { 36 | fn from(arg: &str) -> Self { 37 | return Error::E(arg.to_string()); 38 | } 39 | } 40 | 41 | impl From for Error { 42 | fn from(arg: String) -> Self { 43 | return Error::E(arg); 44 | } 45 | } 46 | 47 | impl From<&dyn std::error::Error> for Error { 48 | fn from(arg: &dyn std::error::Error) -> Self { 49 | return Error::E(arg.to_string()); 50 | } 51 | } 52 | 53 | impl Clone for Error { 54 | fn clone(&self) -> Self { 55 | Error::from(self.to_string()) 56 | } 57 | 58 | fn clone_from(&mut self, source: &Self) { 59 | *self = Self::from(source.to_string()); 60 | } 61 | } 62 | 63 | pub trait OptionToResult { 64 | fn to_result(self, error_str: &str) -> Result; 65 | } 66 | 67 | impl OptionToResult for Option { 68 | fn to_result(self, error_str: &str) -> Result { 69 | if self.is_some() { 70 | Ok(self.unwrap()) 71 | } else { 72 | Err(Error::from(error_str)) 73 | } 74 | } 75 | } 76 | 77 | // #[test] 78 | // fn test_json_error() { 79 | // let e = Error::from("rust"); 80 | // let s = serde_json::to_string(&e).unwrap(); 81 | // println!("{}", s.as_str()); 82 | // } 83 | -------------------------------------------------------------------------------- /mybatis-sql/src/ops_bit_and.rs: -------------------------------------------------------------------------------- 1 | use crate::ops::BitAnd; 2 | use crate::ops::Value; 3 | 4 | impl BitAnd for Value { 5 | type Output = bool; 6 | 7 | fn op_bitand(self, rhs: Self) -> Self::Output { 8 | self.as_bool().unwrap_or(false) & rhs.as_bool().unwrap_or(false) 9 | } 10 | } 11 | 12 | impl BitAnd for bool { 13 | type Output = bool; 14 | 15 | fn op_bitand(self, rhs: Value) -> Self::Output { 16 | self & rhs.as_bool().unwrap_or(false) 17 | } 18 | } 19 | 20 | //ref value 21 | impl BitAnd for &Value { 22 | type Output = bool; 23 | 24 | fn op_bitand(self, rhs: Value) -> Self::Output { 25 | self.as_bool().unwrap_or(false) & rhs.as_bool().unwrap_or(false) 26 | } 27 | } 28 | 29 | impl BitAnd<&Value> for &Value { 30 | type Output = bool; 31 | 32 | fn op_bitand(self, rhs: &Value) -> Self::Output { 33 | self.as_bool().unwrap_or(false) & rhs.as_bool().unwrap_or(false) 34 | } 35 | } 36 | 37 | impl BitAnd<&&Value> for &Value { 38 | type Output = bool; 39 | 40 | fn op_bitand(self, rhs: &&Value) -> Self::Output { 41 | self.as_bool().unwrap_or(false) & rhs.as_bool().unwrap_or(false) 42 | } 43 | } 44 | 45 | impl BitAnd for &Value { 46 | type Output = bool; 47 | 48 | fn op_bitand(self, rhs: bool) -> Self::Output { 49 | self.as_bool().unwrap_or(false) & rhs 50 | } 51 | } 52 | 53 | //rhs ref 54 | impl BitAnd<&Value> for Value { 55 | type Output = bool; 56 | 57 | fn op_bitand(self, rhs: &Value) -> Self::Output { 58 | self.as_bool().unwrap_or(false) & rhs.as_bool().unwrap_or(false) 59 | } 60 | } 61 | 62 | impl BitAnd<&Value> for bool { 63 | type Output = bool; 64 | 65 | fn op_bitand(self, rhs: &Value) -> Self::Output { 66 | self & rhs.as_bool().unwrap_or(false) 67 | } 68 | } 69 | 70 | impl BitAnd<&&Value> for Value { 71 | type Output = bool; 72 | 73 | fn op_bitand(self, rhs: &&Value) -> Self::Output { 74 | self.as_bool().unwrap_or(false) & rhs.as_bool().unwrap_or(false) 75 | } 76 | } 77 | 78 | impl BitAnd<&&Value> for bool { 79 | type Output = bool; 80 | 81 | fn op_bitand(self, rhs: &&Value) -> Self::Output { 82 | self & rhs.as_bool().unwrap_or(false) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /mybatis-sql/src/sql_replace.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! push_index { 3 | ($n:expr,$new_sql:ident,$index:expr) => {{ 4 | let num = $index / $n; 5 | $new_sql.push((num + 48) as u8 as char); 6 | $index % $n 7 | }}; 8 | ($index:ident,$new_sql:ident) => { 9 | if $index >= 0 && $index < 10 { 10 | $new_sql.push(($index + 48) as u8 as char); 11 | } else if $index >= 10 && $index < 100 { 12 | let $index = push_index!(10, $new_sql, $index); 13 | let $index = push_index!(1, $new_sql, $index); 14 | } else if $index >= 100 && $index < 1000 { 15 | let $index = push_index!(100, $new_sql, $index); 16 | let $index = push_index!(10, $new_sql, $index); 17 | let $index = push_index!(1, $new_sql, $index); 18 | } else if $index >= 1000 && $index < 10000 { 19 | let $index = push_index!(1000, $new_sql, $index); 20 | let $index = push_index!(100, $new_sql, $index); 21 | let $index = push_index!(10, $new_sql, $index); 22 | let $index = push_index!(1, $new_sql, $index); 23 | } else { 24 | use std::fmt::Write; 25 | $new_sql 26 | .write_fmt(format_args!("{}", $index)) 27 | .expect("a Display implementation returned an error unexpectedly"); 28 | } 29 | }; 30 | } 31 | 32 | #[macro_export] 33 | macro_rules! sql_index { 34 | ($sql:ident,$format_char:expr) => { 35 | let mut new_sql = String::with_capacity($sql.len() + 20); 36 | let mut string_start = false; 37 | let mut index: i32 = 0; 38 | for x in $sql.chars() { 39 | if x == '\'' || x == '"' { 40 | if string_start == true { 41 | string_start = false; 42 | new_sql.push(x); 43 | continue; 44 | } 45 | string_start = true; 46 | new_sql.push(x); 47 | continue; 48 | } 49 | if string_start { 50 | new_sql.push(x); 51 | } else { 52 | if x == '?' && $format_char != '?' { 53 | index += 1; 54 | new_sql.push($format_char); 55 | push_index!(index, new_sql); 56 | } else { 57 | new_sql.push(x); 58 | } 59 | } 60 | } 61 | $sql = new_sql 62 | }; 63 | } 64 | -------------------------------------------------------------------------------- /mybatis-macro/src/string_util.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{HashSet, LinkedList}; 2 | 3 | //find like #{*,*},${*,*} value * 4 | pub fn find_convert_string(arg: &str) -> LinkedList<(String, String)> { 5 | let mut list = LinkedList::new(); 6 | let chars: Vec = arg.bytes().collect(); 7 | let mut item = String::new(); 8 | let mut last_index: i32 = -1; 9 | let mut index: i32 = -1; 10 | for v in &chars { 11 | index = index + 1; 12 | if last_index == -1 && (*v == '#' as u8 || *v == '$' as u8) { 13 | let next = chars.get(index as usize + 1); 14 | let next_char = '{' as u8; 15 | if next.is_some() && next.unwrap().eq(&next_char) { 16 | last_index = index; 17 | } 18 | continue; 19 | } 20 | if *v == '}' as u8 && last_index != -1 { 21 | item = String::from_utf8(chars[(last_index + 2) as usize..index as usize].to_vec()) 22 | .unwrap(); 23 | let value = 24 | String::from_utf8(chars[last_index as usize..(index + 1) as usize].to_vec()) 25 | .unwrap(); 26 | list.push_back((item.clone(), value)); 27 | item.clear(); 28 | last_index = -1; 29 | } 30 | } 31 | return list; 32 | } 33 | 34 | pub fn count_string_num(s: &String, c: char) -> usize { 35 | let cs = s.chars(); 36 | let mut num = 0; 37 | for x in cs { 38 | if x == c { 39 | num += 1; 40 | } 41 | } 42 | return num; 43 | } 44 | 45 | pub fn to_snake_name(name: &str) -> String { 46 | let chs = name.chars(); 47 | let mut new_name = String::new(); 48 | let mut index = 0; 49 | let chs_len = name.len(); 50 | for x in chs { 51 | if x.is_uppercase() { 52 | if index != 0 && (index + 1) != chs_len { 53 | new_name.push_str("_"); 54 | } 55 | new_name.push_str(x.to_lowercase().to_string().as_str()); 56 | } else { 57 | new_name.push(x); 58 | } 59 | index += 1; 60 | } 61 | return new_name; 62 | } 63 | 64 | ///input 'strings' => strings 65 | pub fn un_packing_string(column: &str) -> &str { 66 | if column.len() >= 2 { 67 | if column.starts_with("'") && column.ends_with("'") { 68 | return &column[1..column.len() - 1]; 69 | } 70 | if column.starts_with("`") && column.ends_with("`") { 71 | return &column[1..column.len() - 1]; 72 | } 73 | if column.starts_with("\"") && column.ends_with("\"") { 74 | return &column[1..column.len() - 1]; 75 | } 76 | } 77 | return column; 78 | } 79 | -------------------------------------------------------------------------------- /mybatis-macro/src/html_loader.rs: -------------------------------------------------------------------------------- 1 | use html_parser::{Dom, Node, Result}; 2 | use std::collections::HashMap; 3 | use std::fmt::{Debug, Formatter}; 4 | 5 | #[derive(Clone, Eq, PartialEq)] 6 | pub struct Element { 7 | pub tag: String, 8 | pub data: String, 9 | pub attrs: HashMap, 10 | pub childs: Vec, 11 | } 12 | 13 | impl Debug for Element { 14 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 15 | let mut s = f.debug_struct(""); 16 | match self.tag.as_str() { 17 | "" => { 18 | s.field("data", &self.data); 19 | } 20 | _ => { 21 | s.field("tag", &self.tag); 22 | if !self.attrs.is_empty() { 23 | s.field("attributes", &self.attrs); 24 | } 25 | if !self.childs.is_empty() { 26 | s.field("childs", &self.childs); 27 | } 28 | } 29 | } 30 | return s.finish(); 31 | } 32 | } 33 | 34 | pub fn as_element(args: &Vec) -> Vec { 35 | let mut els = vec![]; 36 | for x in args { 37 | let mut el = Element { 38 | tag: "".to_string(), 39 | data: "".to_string(), 40 | attrs: HashMap::new(), 41 | childs: vec![], 42 | }; 43 | match x { 44 | Node::Text(txt) => { 45 | if txt.is_empty() { 46 | continue; 47 | } 48 | el.data = txt.to_string(); 49 | } 50 | Node::Element(element) => { 51 | el.tag = element.name.to_string(); 52 | if element.id.is_some() { 53 | el.attrs.insert( 54 | "id".to_string(), 55 | element.id.as_ref().unwrap_or(&String::new()).clone(), 56 | ); 57 | } 58 | for (k, v) in &element.attributes { 59 | el.attrs 60 | .insert(k.clone(), v.as_ref().unwrap_or(&String::new()).clone()); 61 | } 62 | if !element.children.is_empty() { 63 | let childs = as_element(&element.children); 64 | el.childs = childs; 65 | } 66 | } 67 | Node::Comment(comment) => { 68 | println!("comment:{}", comment); 69 | } 70 | } 71 | els.push(el); 72 | } 73 | els 74 | } 75 | 76 | pub fn load_html(html: &str) -> Result> { 77 | let dom = Dom::parse(html)?; 78 | let els = as_element(&dom.children); 79 | return Ok(els); 80 | } 81 | -------------------------------------------------------------------------------- /mybatis-core/src/db/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "mssql")] 2 | pub mod bind_mssql; 3 | #[cfg(feature = "mysql")] 4 | pub mod bind_mysql; 5 | #[cfg(feature = "postgres")] 6 | pub mod bind_pg; 7 | #[cfg(feature = "sqlite")] 8 | pub mod bind_sqlite; 9 | 10 | use rbson::Bson; 11 | use std::time::Duration; 12 | 13 | use chrono::NaiveDateTime; 14 | use serde::de::DeserializeOwned; 15 | use serde::{Deserialize, Serialize}; 16 | 17 | use crate::convert::StmtConvert; 18 | use crate::db::db_adapter::DataDecoder; 19 | pub use db_adapter::{DBConnectOption, DBExecResult, DBPool, DBPoolConn, DBQuery, DBTx}; 20 | 21 | pub mod db_adapter; 22 | 23 | #[derive(Debug)] 24 | pub struct DBPoolOptions { 25 | pub max_connections: u32, 26 | pub min_connections: u32, 27 | pub connect_timeout: Duration, 28 | pub max_lifetime: Option, 29 | pub idle_timeout: Option, 30 | pub test_before_acquire: bool, 31 | pub decoder: Box, 32 | } 33 | 34 | impl Default for DBPoolOptions { 35 | fn default() -> Self { 36 | Self { 37 | // pool a maximum of 10 connections to the same database 38 | max_connections: 10, 39 | // don't open connections until necessary 40 | min_connections: 0, 41 | // try to connect for 10 seconds before erroring 42 | connect_timeout: Duration::from_secs(60), 43 | // reap connections that have been alive > 30 minutes 44 | // prevents unbounded live-leaking of memory due to naive prepared statement caching 45 | // see src/cache.rs for context 46 | max_lifetime: Some(Duration::from_secs(1800)), 47 | // don't reap connections based on idle time 48 | idle_timeout: None, 49 | // If true, test the health of a connection on acquire 50 | test_before_acquire: true, 51 | decoder: Box::new(DefaultDecoder {}), 52 | } 53 | } 54 | } 55 | 56 | impl DBPoolOptions { 57 | pub fn new() -> Self { 58 | DBPoolOptions::default() 59 | } 60 | } 61 | 62 | #[derive(Clone, Debug)] 63 | pub struct DefaultDecoder {} 64 | 65 | impl DataDecoder for DefaultDecoder { 66 | fn decode(&self, _key: &str, _data: &mut Bson) -> crate::Result<()> { 67 | return Ok(()); 68 | } 69 | } 70 | 71 | #[derive(Serialize, Deserialize, Debug, Clone, Copy, Eq, PartialEq)] 72 | pub enum DriverType { 73 | None = 0, 74 | Mysql = 1, 75 | Postgres = 2, 76 | Sqlite = 3, 77 | Mssql = 4, 78 | } 79 | 80 | impl DriverType { 81 | pub fn is_number_type(&self) -> bool { 82 | match self { 83 | DriverType::Postgres | DriverType::Mssql => { 84 | return true; 85 | } 86 | _ => { 87 | return false; 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /mybatis/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mybatis" 3 | version = "2.0.4" 4 | edition = "2021" 5 | rust-version = "1.59.0" 6 | authors = ["James Zow "] 7 | license = "Apache-2.0" 8 | description = "mybatis" 9 | readme = "README.md" 10 | keywords = ["mybatis", "orm", "postgres", "mysql", "sqlite"] 11 | documentation = "https://github.com/summer-rust/summer-mybatis#readme" 12 | repository = "https://github.com/summer-rust/summer-mybatis" 13 | homepage = "https://github.com/summer-rust/summer-mybatis#readme" 14 | 15 | [features] 16 | default = ["default_mode", "all-database"] 17 | #debug_mode feature will show decode json data 18 | debug_mode = ["mybatis-core/debug_mode", "mybatis-macro/debug_mode", "mybatis-sql/debug_mode"] 19 | default_mode = ["mybatis-core", "mybatis-macro", "mybatis-sql"] 20 | #support upper case sql keyword 21 | upper_case_sql_keyword = [] 22 | #runtime database 23 | all-database = ["default_mode", "mybatis-core/all-database"] 24 | mysql = ["default_mode", "mybatis-core/mysql"] 25 | postgres = ["default_mode", "mybatis-core/postgres"] 26 | sqlite = ["default_mode", "mybatis-core/sqlite"] 27 | mssql = ["default_mode", "mybatis-core/mssql"] 28 | 29 | #choose runtime 30 | runtime-tokio-rustls = ["mybatis-core/runtime-tokio-rustls"] 31 | runtime-actix-rustls = ["mybatis-core/runtime-actix-rustls"] 32 | runtime-async-std-rustls = ["mybatis-core/runtime-async-std-rustls"] 33 | runtime-tokio-native-tls = ["mybatis-core/runtime-tokio-native-tls"] 34 | runtime-actix-native-tls = ["mybatis-core/runtime-actix-native-tls"] 35 | runtime-async-std-native-tls = ["mybatis-core/runtime-async-std-native-tls"] 36 | 37 | #enable_format bson 38 | format_bson = ["mybatis-core/format_bson"] 39 | 40 | [dependencies] 41 | mybatis-util = { version = "2.0.0", path = "../mybatis-util", default-features = false} 42 | mybatis-sql = { version = "2.0.1", path = "../mybatis-sql", default-features = false, optional = true} 43 | mybatis-core = { version = "2.0.1", path = "../mybatis-core", default-features = false, optional = true} 44 | mybatis-macro = { version = "2.0.3", path = "../mybatis-macro", default-features = false, optional = true} 45 | 46 | ################################################################ 47 | rbson = { version = "2.0.4", features = ["uuid-0_8", "chrono-0_4"] } 48 | chrono = { version = "0.4", features = ["serde"] } 49 | #serde 50 | serde = { version = "1", features = ["derive"] } 51 | #uuid 52 | uuid = { version = "0.8", features = ["serde", "v4"] } 53 | #log 54 | log = { version = "0.4", default-features = false } 55 | #async trait 56 | async-trait = { version = "0.1.53"} 57 | futures-core = { version = "0.3" } 58 | futures = { version = "0.3" } 59 | once_cell = "1.10" 60 | #object_id 61 | hex = "0.4" 62 | rand = "0.8" 63 | 64 | [dev-dependencies] 65 | serde_json = "1" 66 | 67 | [profile.release] 68 | lto = true 69 | opt-level = 3 70 | codegen-units = 1 71 | -------------------------------------------------------------------------------- /mybatis-sql/src/string_util.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{HashSet, LinkedList}; 2 | 3 | //find like #{*,*},${*,*} value * 4 | pub fn find_convert_string(arg: &str) -> LinkedList<(String, String)> { 5 | let mut list = LinkedList::new(); 6 | let mut cache = HashSet::new(); 7 | let chars: Vec = arg.bytes().collect(); 8 | let mut item = String::new(); 9 | let mut last_index: i32 = -1; 10 | let mut index: i32 = -1; 11 | for v in &chars { 12 | index = index + 1; 13 | if last_index == -1 && (*v == '#' as u8 || *v == '$' as u8) { 14 | let next = chars.get(index as usize + 1); 15 | let next_char = '{' as u8; 16 | if next.is_some() && next.unwrap().eq(&next_char) { 17 | last_index = index; 18 | } 19 | continue; 20 | } 21 | if *v == '}' as u8 && last_index != -1 { 22 | item = String::from_utf8(chars[(last_index + 2) as usize..index as usize].to_vec()) 23 | .unwrap(); 24 | if cache.get(&item).is_some() { 25 | item.clear(); 26 | last_index = -1; 27 | continue; 28 | } 29 | let value = 30 | String::from_utf8(chars[last_index as usize..(index + 1) as usize].to_vec()) 31 | .unwrap(); 32 | cache.insert(item.clone()); 33 | list.push_back((item.clone(), value)); 34 | item.clear(); 35 | last_index = -1; 36 | } 37 | } 38 | return list; 39 | } 40 | 41 | pub fn count_string_num(s: &String, c: char) -> usize { 42 | let cs = s.chars(); 43 | let mut num = 0; 44 | for x in cs { 45 | if x == c { 46 | num += 1; 47 | } 48 | } 49 | return num; 50 | } 51 | 52 | pub fn to_snake_name(name: &str) -> String { 53 | let chs = name.chars(); 54 | let mut new_name = String::new(); 55 | let mut index = 0; 56 | let chs_len = name.len(); 57 | for x in chs { 58 | if x.is_uppercase() { 59 | if index != 0 && (index + 1) != chs_len { 60 | new_name.push_str("_"); 61 | } 62 | new_name.push_str(x.to_lowercase().to_string().as_str()); 63 | } else { 64 | new_name.push(x); 65 | } 66 | index += 1; 67 | } 68 | return new_name; 69 | } 70 | 71 | ///input 'strings' => strings 72 | pub fn un_packing_string(column: &str) -> &str { 73 | if column.len() >= 2 { 74 | if column.starts_with("'") && column.ends_with("'") { 75 | return &column[1..column.len() - 1]; 76 | } 77 | if column.starts_with("`") && column.ends_with("`") { 78 | return &column[1..column.len() - 1]; 79 | } 80 | if column.starts_with("\"") && column.ends_with("\"") { 81 | return &column[1..column.len() - 1]; 82 | } 83 | } 84 | return column; 85 | } 86 | -------------------------------------------------------------------------------- /mybatis-sql/src/template.rs: -------------------------------------------------------------------------------- 1 | use once_cell::sync::Lazy; 2 | 3 | pub static TEMPLATE: Lazy = Lazy::new(|| SqlTemplates::default()); 4 | 5 | #[inline] 6 | fn string_to_static_str(s: String) -> &'static str { 7 | Box::leak(s.into_boxed_str()) 8 | } 9 | 10 | #[derive(Clone, Debug)] 11 | pub struct Keywords { 12 | pub value: &'static str, 13 | pub left_space: &'static str, 14 | pub right_space: &'static str, 15 | pub left_right_space: &'static str, 16 | } 17 | 18 | macro_rules! gen_template { 19 | ({ $($key:ident:$value:tt$(,)?)+ }) => { 20 | /// Most of the SQL keywords used by the mybatis 21 | #[derive(Clone,Debug)] 22 | pub struct SqlTemplates { 23 | $(pub $key:Keywords, 24 | )+ 25 | } 26 | impl Default for SqlTemplates { 27 | fn default() -> Self { 28 | if cfg!(feature = "upper_case_sql_keyword") { 29 | Self{ 30 | $( 31 | $key:Keywords{ 32 | value: string_to_static_str($value.to_uppercase()), 33 | left_space:string_to_static_str(" ".to_string()+$value.to_uppercase().as_str()), 34 | right_space:string_to_static_str($value.to_uppercase()+" "), 35 | left_right_space:string_to_static_str(format!(" {} ",$value.to_uppercase())), 36 | }, 37 | )+ 38 | } 39 | }else{ 40 | Self{ 41 | $( 42 | $key:Keywords{ 43 | value:concat!("",$value,""), 44 | left_space:concat!(" ",$value,""), 45 | right_space:concat!("",$value," "), 46 | left_right_space:concat!(" ",$value," "), 47 | }, 48 | )+ 49 | } 50 | } 51 | 52 | 53 | } 54 | } 55 | } 56 | } 57 | 58 | gen_template!({ 59 | r#where: "where", 60 | and: "and", 61 | or: "or", 62 | r#in: "in", 63 | having: "having", 64 | order_by: "order by", 65 | group_by: "group by", 66 | asc: "asc", 67 | desc: "desc", 68 | between: "between", 69 | not: "not", 70 | like: "like", 71 | is: "is", 72 | null: "NULL", 73 | insert_into: "insert into", 74 | values: "values", 75 | limit: "limit", 76 | set: "set", 77 | update: "update", 78 | select: "select", 79 | delete_from: "delete from", 80 | from: "from", 81 | r#as: "as", 82 | offset: "offset", 83 | rows_fetch_next: "rows fetch next", 84 | rows_only: "rows only", 85 | }); 86 | 87 | #[test] 88 | fn test_gen() { 89 | let t = SqlTemplates::default(); 90 | println!("{:?}", t); 91 | } 92 | -------------------------------------------------------------------------------- /mybatis-core/src/convert.rs: -------------------------------------------------------------------------------- 1 | use crate::db::db_adapter::DataDecoder; 2 | use crate::db::DriverType; 3 | use crate::Result; 4 | use rbson::Bson; 5 | 6 | ///the stmt replace str convert 7 | pub trait StmtConvert { 8 | fn stmt_convert(&self, index: usize, item: &mut String); 9 | } 10 | 11 | #[macro_export] 12 | macro_rules! push_index { 13 | ($n:expr,$new_sql:ident,$index:expr) => {{ 14 | let num = $index / $n; 15 | $new_sql.push((num + 48) as u8 as char); 16 | $index % $n 17 | }}; 18 | ($index:ident,$new_sql:ident) => { 19 | if $index >= 0 && $index < 10 { 20 | $new_sql.push(($index + 48) as u8 as char); 21 | } else if $index >= 10 && $index < 100 { 22 | let $index = push_index!(10, $new_sql, $index); 23 | let $index = push_index!(1, $new_sql, $index); 24 | } else if $index >= 100 && $index < 1000 { 25 | let $index = push_index!(100, $new_sql, $index); 26 | let $index = push_index!(10, $new_sql, $index); 27 | let $index = push_index!(1, $new_sql, $index); 28 | } else if $index >= 1000 && $index < 10000 { 29 | let $index = push_index!(1000, $new_sql, $index); 30 | let $index = push_index!(100, $new_sql, $index); 31 | let $index = push_index!(10, $new_sql, $index); 32 | let $index = push_index!(1, $new_sql, $index); 33 | } else { 34 | use std::fmt::Write; 35 | $new_sql 36 | .write_fmt(format_args!("{}", $index)) 37 | .expect("a Display implementation returned an error unexpectedly"); 38 | } 39 | }; 40 | } 41 | 42 | impl StmtConvert for DriverType { 43 | fn stmt_convert(&self, index: usize, item: &mut String) { 44 | match &self { 45 | DriverType::Postgres => { 46 | item.push('$'); 47 | let index = index + 1; 48 | push_index!(index, item); 49 | } 50 | DriverType::Mysql => { 51 | item.push('?'); 52 | } 53 | DriverType::Sqlite => { 54 | item.push('?'); 55 | } 56 | DriverType::Mssql => { 57 | item.push('@'); 58 | item.push('p'); 59 | let index = index + 1; 60 | push_index!(index, item); 61 | } 62 | DriverType::None => { 63 | panic!("[mybatis] un support none for driver type!") 64 | } 65 | } 66 | } 67 | } 68 | 69 | ///json convert 70 | pub trait JsonCodec { 71 | /// to an json value 72 | fn try_to_bson(self) -> Result; 73 | } 74 | 75 | ///json convert 76 | pub trait RefJsonCodec { 77 | /// to an json value 78 | fn try_to_bson(&self, decoder: &dyn DataDecoder) -> Result; 79 | } 80 | 81 | ///result convert 82 | pub trait ResultCodec { 83 | fn into_result(self) -> Result; 84 | } 85 | 86 | #[macro_export] 87 | macro_rules! to_bson_macro { 88 | ($r:ident) => {{ 89 | if $r.is_some() { 90 | rbson::bson!($r.unwrap()) 91 | } else { 92 | rbson::Bson::Null 93 | } 94 | }}; 95 | } 96 | -------------------------------------------------------------------------------- /mybatis/src/log.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Deref; 2 | 3 | use log::{debug, error, info, trace, warn, LevelFilter}; 4 | use std::fmt::{Debug, Display}; 5 | 6 | /// log plugin 7 | pub trait LogPlugin: Send + Sync + Debug { 8 | ///the name 9 | fn name(&self) -> &str { 10 | std::any::type_name::() 11 | } 12 | fn get_level_filter(&self) -> &LevelFilter; 13 | fn is_enable(&self) -> bool { 14 | return !self.get_level_filter().eq(&log::LevelFilter::Off); 15 | } 16 | fn do_log(&self, id: i64, data: &str) { 17 | match self.get_level_filter() { 18 | log::LevelFilter::Error => { 19 | self.error(id, data); 20 | } 21 | log::LevelFilter::Warn => { 22 | self.warn(id, data); 23 | } 24 | log::LevelFilter::Info => { 25 | self.info(id, data); 26 | } 27 | log::LevelFilter::Debug => { 28 | self.debug(id, data); 29 | } 30 | log::LevelFilter::Trace => { 31 | self.trace(id, data); 32 | } 33 | log::LevelFilter::Off => {} 34 | } 35 | } 36 | 37 | fn error(&self, id: i64, data: &str) { 38 | let filter = self.get_level_filter(); 39 | if filter.eq(&LevelFilter::Off) { 40 | return; 41 | } 42 | if filter.ge(&LevelFilter::Error) { 43 | error!("[mybatis] [{}] {}", id, data); 44 | } 45 | } 46 | 47 | fn warn(&self, id: i64, data: &str) { 48 | let filter = self.get_level_filter(); 49 | if filter.eq(&LevelFilter::Off) { 50 | return; 51 | } 52 | if filter.ge(&LevelFilter::Warn) { 53 | warn!("[mybatis] [{}] {}", id, data); 54 | } 55 | } 56 | 57 | fn info(&self, id: i64, data: &str) { 58 | let filter = self.get_level_filter(); 59 | if filter.eq(&LevelFilter::Off) { 60 | return; 61 | } 62 | if filter.ge(&LevelFilter::Info) { 63 | info!("[mybatis] [{}] {}", id, data); 64 | } 65 | } 66 | 67 | fn debug(&self, id: i64, data: &str) { 68 | let filter = self.get_level_filter(); 69 | if filter.eq(&LevelFilter::Off) { 70 | return; 71 | } 72 | if filter.ge(&LevelFilter::Debug) { 73 | debug!("[mybatis] [{}] {}", id, data); 74 | } 75 | } 76 | 77 | fn trace(&self, id: i64, data: &str) { 78 | let filter = self.get_level_filter(); 79 | if filter.eq(&LevelFilter::Off) { 80 | return; 81 | } 82 | if filter.ge(&LevelFilter::Trace) { 83 | trace!("[mybatis] [{}] {}", id, data); 84 | } 85 | } 86 | } 87 | 88 | #[derive(Debug)] 89 | pub struct MyBatisLogPlugin { 90 | pub level_filter: LevelFilter, 91 | } 92 | 93 | impl Default for MyBatisLogPlugin { 94 | fn default() -> Self { 95 | Self { 96 | level_filter: log::LevelFilter::Info, 97 | } 98 | } 99 | } 100 | 101 | impl LogPlugin for MyBatisLogPlugin { 102 | fn get_level_filter(&self) -> &LevelFilter { 103 | &self.level_filter 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /mybatis-core/src/types/bytes.rs: -------------------------------------------------------------------------------- 1 | use rbson::Bson; 2 | use serde::de::Error; 3 | use serde::{Deserializer, Serializer}; 4 | use std::ops::{Deref, DerefMut}; 5 | 6 | /// Mybatis Bytes 7 | #[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] 8 | pub struct Bytes { 9 | pub inner: Vec, 10 | } 11 | 12 | impl From for Bytes { 13 | fn from(arg: rbson::Binary) -> Self { 14 | Self { inner: arg.bytes } 15 | } 16 | } 17 | 18 | impl From<&rbson::Binary> for Bytes { 19 | fn from(arg: &rbson::Binary) -> Self { 20 | Self { 21 | inner: arg.bytes.clone(), 22 | } 23 | } 24 | } 25 | 26 | impl From<&[u8]> for Bytes { 27 | fn from(arg: &[u8]) -> Self { 28 | Self { 29 | inner: arg.to_owned(), 30 | } 31 | } 32 | } 33 | 34 | impl From> for Bytes { 35 | fn from(arg: Vec) -> Self { 36 | Self { inner: arg } 37 | } 38 | } 39 | 40 | impl From<&Vec> for Bytes { 41 | fn from(arg: &Vec) -> Self { 42 | Self { 43 | inner: arg.to_owned(), 44 | } 45 | } 46 | } 47 | 48 | impl serde::Serialize for Bytes { 49 | #[inline] 50 | fn serialize(&self, serializer: S) -> Result 51 | where 52 | S: Serializer, 53 | { 54 | serializer.serialize_bytes(&self.inner) 55 | } 56 | } 57 | 58 | impl<'de> serde::Deserialize<'de> for Bytes { 59 | #[inline] 60 | fn deserialize(deserializer: D) -> Result 61 | where 62 | D: Deserializer<'de>, 63 | { 64 | let bson = Bson::deserialize(deserializer)?; 65 | match bson { 66 | Bson::Binary(data) => { 67 | return Ok(Bytes { inner: data.bytes }); 68 | } 69 | Bson::String(data) => { 70 | return match base64::decode(data) { 71 | Ok(v) => Ok(Bytes { inner: v }), 72 | Err(e) => { 73 | return Err(D::Error::custom(e.to_string())); 74 | } 75 | }; 76 | } 77 | _ => Err(D::Error::custom("deserialize unsupported bson type!")), 78 | } 79 | } 80 | } 81 | 82 | impl Bytes { 83 | pub fn new(arg: Vec) -> Bytes { 84 | Bytes { inner: arg } 85 | } 86 | } 87 | 88 | impl Deref for Bytes { 89 | type Target = Vec; 90 | 91 | fn deref(&self) -> &Self::Target { 92 | &self.inner 93 | } 94 | } 95 | 96 | impl DerefMut for Bytes { 97 | fn deref_mut(&mut self) -> &mut Self::Target { 98 | &mut self.inner 99 | } 100 | } 101 | 102 | #[cfg(test)] 103 | mod test { 104 | use crate::types::Bytes; 105 | use rbson::Bson; 106 | 107 | #[test] 108 | fn test_ser_de() { 109 | let b = Bytes::from("111".as_bytes()); 110 | let bsons = rbson::to_bson(&b).unwrap(); 111 | match &bsons { 112 | rbson::Bson::Binary(b) => { 113 | assert_eq!(b.subtype, rbson::spec::BinarySubtype::Generic); 114 | println!("yes is BinarySubtype::Generic"); 115 | } 116 | _ => {} 117 | } 118 | let b_de: Bytes = rbson::from_bson(bsons).unwrap(); 119 | assert_eq!(b, b_de); 120 | assert_eq!(b.inner, b_de.inner); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /mybatis/src/snowflake.rs: -------------------------------------------------------------------------------- 1 | use std::sync::atomic::{AtomicI64, Ordering}; 2 | use std::sync::Arc; 3 | 4 | use chrono::Utc; 5 | use once_cell::sync::Lazy; 6 | 7 | ///Snowflakes algorithm 8 | #[derive(Debug, serde::Serialize, serde::Deserialize)] 9 | pub struct Snowflake { 10 | pub epoch: i64, 11 | pub worker_id: i64, 12 | pub datacenter_id: i64, 13 | pub sequence: AtomicI64, 14 | pub time: AtomicI64, 15 | } 16 | 17 | impl Clone for Snowflake { 18 | fn clone(&self) -> Self { 19 | let sequence = self.sequence.load(Ordering::Relaxed); 20 | let time = self.time.load(Ordering::Relaxed); 21 | Self { 22 | epoch: self.epoch.clone(), 23 | worker_id: self.worker_id.clone(), 24 | datacenter_id: self.datacenter_id.clone(), 25 | sequence: AtomicI64::new(sequence), 26 | time: AtomicI64::new(time), 27 | } 28 | } 29 | } 30 | 31 | impl Snowflake { 32 | pub fn default() -> Snowflake { 33 | Snowflake { 34 | epoch: 1_564_790_400_000, 35 | worker_id: 1, 36 | datacenter_id: 1, 37 | sequence: AtomicI64::new(0), 38 | time: AtomicI64::new(0), 39 | } 40 | } 41 | 42 | pub fn new(epoch: i64, worker_id: i64, datacenter_id: i64) -> Snowflake { 43 | Snowflake { 44 | epoch, 45 | worker_id, 46 | datacenter_id, 47 | sequence: AtomicI64::new(0), 48 | time: AtomicI64::new(0), 49 | } 50 | } 51 | 52 | pub fn set_epoch(&mut self, epoch: i64) -> &mut Self { 53 | self.epoch = epoch; 54 | self 55 | } 56 | 57 | pub fn set_worker_id(&mut self, worker_id: i64) -> &mut Self { 58 | self.worker_id = worker_id; 59 | self 60 | } 61 | 62 | pub fn set_datacenter_id(&mut self, datacenter_id: i64) -> &mut Self { 63 | self.datacenter_id = datacenter_id; 64 | self 65 | } 66 | 67 | pub fn generate(&self) -> i64 { 68 | let last_timestamp = self.time.fetch_or(0, Ordering::Relaxed); 69 | let mut timestamp = self.get_time(); 70 | let sequence = self.sequence.fetch_or(0, Ordering::Relaxed); 71 | if timestamp == last_timestamp { 72 | let sequence = (sequence + 1) & (-1 ^ (-1 << 12)); 73 | self.sequence.swap(sequence, Ordering::Relaxed); 74 | if sequence == 0 && timestamp <= last_timestamp { 75 | timestamp = self.get_time(); 76 | } 77 | } else { 78 | self.sequence.swap(0, Ordering::Relaxed); 79 | } 80 | self.time.swap(timestamp, Ordering::Relaxed); 81 | (timestamp << 22) 82 | | (self.worker_id << 17) 83 | | (self.datacenter_id << 12) 84 | | self.sequence.fetch_or(0, Ordering::Relaxed) 85 | } 86 | 87 | fn get_time(&self) -> i64 { 88 | Utc::now().timestamp_millis() - self.epoch 89 | } 90 | } 91 | 92 | pub static SNOWFLAKE: Lazy = Lazy::new(|| Snowflake::default()); 93 | 94 | ///gen new snowflake_id 95 | pub fn new_snowflake_id() -> i64 { 96 | SNOWFLAKE.generate() 97 | } 98 | 99 | #[cfg(test)] 100 | mod test { 101 | use crate::snowflake::new_snowflake_id; 102 | 103 | #[test] 104 | fn test_gen() { 105 | println!("{}", new_snowflake_id()); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /mybatis-sql/src/error.rs: -------------------------------------------------------------------------------- 1 | //! Errorand Result types. 2 | use std::error::Error as StdError; 3 | use std::fmt::{self, Debug, Display}; 4 | use std::io; 5 | 6 | use serde::de::Visitor; 7 | use serde::ser::{Serialize, Serializer}; 8 | use serde::{Deserialize, Deserializer}; 9 | 10 | pub type Result = std::result::Result; 11 | 12 | /// A generic error that represents all the ways a method can fail inside of rexpr::core. 13 | #[derive(Debug)] 14 | #[non_exhaustive] 15 | pub enum Error { 16 | /// Default Error 17 | E(String), 18 | } 19 | 20 | impl Display for Error { 21 | // IntellijRust does not understand that [non_exhaustive] applies only for downstream crates 22 | // noinspection RsMatchCheck 23 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 24 | match self { 25 | Error::E(error) => write!(f, "{}", error), 26 | } 27 | } 28 | } 29 | 30 | impl StdError for Error {} 31 | 32 | impl From for Error { 33 | #[inline] 34 | fn from(err: io::Error) -> Self { 35 | Error::from(err.to_string()) 36 | } 37 | } 38 | 39 | impl From<&str> for Error { 40 | fn from(arg: &str) -> Self { 41 | return Error::E(arg.to_string()); 42 | } 43 | } 44 | 45 | impl From for Error { 46 | fn from(arg: String) -> Self { 47 | return Error::E(arg); 48 | } 49 | } 50 | 51 | impl From<&dyn std::error::Error> for Error { 52 | fn from(arg: &dyn std::error::Error) -> Self { 53 | return Error::E(arg.to_string()); 54 | } 55 | } 56 | 57 | impl Clone for Error { 58 | fn clone(&self) -> Self { 59 | Error::from(self.to_string()) 60 | } 61 | 62 | fn clone_from(&mut self, source: &Self) { 63 | *self = Self::from(source.to_string()); 64 | } 65 | } 66 | 67 | // This is what #[derive(Serialize)] would generate. 68 | impl Serialize for Error { 69 | fn serialize(&self, serializer: S) -> std::result::Result 70 | where 71 | S: Serializer, 72 | { 73 | serializer.serialize_str(self.to_string().as_str()) 74 | } 75 | } 76 | 77 | struct ErrorVisitor; 78 | 79 | impl<'de> Visitor<'de> for ErrorVisitor { 80 | type Value = String; 81 | 82 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 83 | formatter.write_str("a string") 84 | } 85 | 86 | fn visit_string(self, v: String) -> std::result::Result 87 | where 88 | E: std::error::Error, 89 | { 90 | Ok(v) 91 | } 92 | 93 | fn visit_str(self, v: &str) -> std::result::Result 94 | where 95 | E: std::error::Error, 96 | { 97 | Ok(v.to_string()) 98 | } 99 | } 100 | 101 | impl<'de> Deserialize<'de> for Error { 102 | fn deserialize(deserializer: D) -> std::result::Result 103 | where 104 | D: Deserializer<'de>, 105 | { 106 | let r = deserializer.deserialize_string(ErrorVisitor)?; 107 | return Ok(Error::from(r)); 108 | } 109 | } 110 | 111 | pub trait OptionToResult { 112 | fn to_result(self, error_str: &str) -> Result; 113 | } 114 | 115 | impl OptionToResult for Option { 116 | fn to_result(self, error_str: &str) -> Result { 117 | if self.is_some() { 118 | Ok(self.unwrap()) 119 | } else { 120 | Err(Error::from(error_str)) 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /mybatis/src/intercept.rs: -------------------------------------------------------------------------------- 1 | use mybatis_core::convert::StmtConvert; 2 | 3 | use crate::mybatis::Mybatis; 4 | use crate::plus::MybatisPlus; 5 | use mybatis_core::db::DriverType; 6 | use mybatis_core::Error; 7 | use mybatis_sql::TEMPLATE; 8 | use rbson::Bson; 9 | use std::fmt::{Debug, Display}; 10 | 11 | /// sql intercept 12 | pub trait SqlIntercept: Send + Sync + Debug { 13 | ///the name 14 | fn name(&self) -> &str { 15 | std::any::type_name::() 16 | } 17 | /// do intercept sql/args 18 | /// is_prepared_sql: if is run in prepared_sql=ture 19 | fn do_intercept( 20 | &self, 21 | rb: &Mybatis, 22 | sql: &mut String, 23 | args: &mut Vec, 24 | is_prepared_sql: bool, 25 | ) -> Result<(), Error>; 26 | } 27 | 28 | #[derive(Debug)] 29 | pub struct MyBatisLogFormatSqlIntercept {} 30 | 31 | impl SqlIntercept for MyBatisLogFormatSqlIntercept { 32 | fn do_intercept( 33 | &self, 34 | rb: &Mybatis, 35 | sql: &mut String, 36 | args: &mut Vec, 37 | is_prepared_sql: bool, 38 | ) -> Result<(), Error> { 39 | let driver_type = rb.driver_type()?; 40 | match driver_type { 41 | DriverType::None => {} 42 | DriverType::Mysql | DriverType::Postgres | DriverType::Sqlite | DriverType::Mssql => { 43 | let mut formated = format!("[format_sql]{}", sql); 44 | for index in 0..args.len() { 45 | let mut data = String::new(); 46 | driver_type.stmt_convert(index, &mut data); 47 | formated = 48 | formated.replacen(&data, &format!("{}", args.get(index).unwrap()), 1); 49 | } 50 | rb.log_plugin.info(0, &formated); 51 | } 52 | } 53 | return Ok(()); 54 | } 55 | } 56 | 57 | /// Prevent full table updates and deletions 58 | #[derive(Debug)] 59 | pub struct BlockAttackDeleteInterceptor {} 60 | 61 | impl SqlIntercept for BlockAttackDeleteInterceptor { 62 | fn do_intercept( 63 | &self, 64 | rb: &Mybatis, 65 | sql: &mut String, 66 | args: &mut Vec, 67 | is_prepared_sql: bool, 68 | ) -> Result<(), Error> { 69 | let sql = sql.trim(); 70 | if sql.starts_with(TEMPLATE.delete_from.value) 71 | && !sql.contains(TEMPLATE.r#where.left_right_space) 72 | { 73 | return Err(Error::from(format!( 74 | "[mybatis][BlockAttackDeleteInterceptor] not allow attack sql:{}", 75 | sql 76 | ))); 77 | } 78 | return Ok(()); 79 | } 80 | } 81 | 82 | /// Prevent full table updates and deletions 83 | #[derive(Debug)] 84 | pub struct BlockAttackUpdateInterceptor {} 85 | 86 | impl SqlIntercept for BlockAttackUpdateInterceptor { 87 | fn do_intercept( 88 | &self, 89 | rb: &Mybatis, 90 | sql: &mut String, 91 | args: &mut Vec, 92 | is_prepared_sql: bool, 93 | ) -> Result<(), Error> { 94 | let sql = sql.trim(); 95 | if sql.starts_with(TEMPLATE.update.value) 96 | && !sql.contains(TEMPLATE.r#where.left_right_space) 97 | { 98 | return Err(Error::from(format!( 99 | "[mybatis][BlockAttackUpdateInterceptor] not allow attack sql:{}", 100 | sql 101 | ))); 102 | } 103 | return Ok(()); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /mybatis-core/src/mssql/sqlx_value.rs: -------------------------------------------------------------------------------- 1 | use rbson::{bson, Bson}; 2 | use sqlx_core::column::Column; 3 | use sqlx_core::decode::Decode; 4 | use sqlx_core::error::BoxDynError; 5 | use sqlx_core::mssql::{Mssql, MssqlRow, MssqlValue, MssqlValueRef}; 6 | use sqlx_core::row::Row; 7 | use sqlx_core::type_info::TypeInfo; 8 | use sqlx_core::types::BigDecimal; 9 | use sqlx_core::value::ValueRef; 10 | 11 | use crate::convert::{JsonCodec, RefJsonCodec, ResultCodec}; 12 | 13 | use crate::db::db_adapter::DataDecoder; 14 | use crate::to_bson_macro; 15 | use crate::types::Decimal; 16 | 17 | impl<'r> JsonCodec for sqlx_core::mssql::MssqlValueRef<'r> { 18 | fn try_to_bson(self) -> crate::Result { 19 | //TODO batter way to match type replace use string match 20 | match self.type_info().name() { 21 | "NULL" => { 22 | return Ok(Bson::Null); 23 | } 24 | "TINYINT" => { 25 | let r: Option = Decode::<'_, Mssql>::decode(self)?; 26 | if let Some(r) = r { 27 | return Ok(bson!(r as i32)); 28 | } 29 | return Ok(Bson::Null); 30 | } 31 | "SMALLINT" => { 32 | let r: Option = Decode::<'_, Mssql>::decode(self)?; 33 | if let Some(r) = r { 34 | return Ok(bson!(r as i32)); 35 | } 36 | return Ok(Bson::Null); 37 | } 38 | "INT" => { 39 | let r: Option = Decode::<'_, Mssql>::decode(self)?; 40 | return Ok(to_bson_macro!(r)); 41 | } 42 | "BIGINT" => { 43 | let r: Option = Decode::<'_, Mssql>::decode(self)?; 44 | return Ok(to_bson_macro!(r)); 45 | } 46 | "REAL" => { 47 | let r: Option = Decode::<'_, Mssql>::decode(self)?; 48 | return Ok(to_bson_macro!(r)); 49 | } 50 | "FLOAT" => { 51 | let r: Option = Decode::<'_, Mssql>::decode(self)?; 52 | return Ok(to_bson_macro!(r)); 53 | } 54 | 55 | "VARCHAR" | "NVARCHAR" | "BIGVARCHAR" | "CHAR" | "BIGCHAR" | "NCHAR" => { 56 | let r: Option = Decode::<'_, Mssql>::decode(self)?; 57 | return Ok(to_bson_macro!(r)); 58 | } 59 | 60 | "NEWDECIMAL" => { 61 | let r: Option = Decode::<'_, Mssql>::decode(self)?; 62 | return Ok(to_bson_macro!(r)); 63 | } 64 | 65 | //TODO "DATE" | "TIME" | "DATETIME" | "TIMESTAMP" => {} 66 | // you can use types package to save date to string 67 | _ => { 68 | return Err(crate::Error::from(format!( 69 | "un support database type for:{:?}!", 70 | self.type_info().name() 71 | ))); 72 | } 73 | } 74 | } 75 | } 76 | 77 | impl RefJsonCodec for Vec { 78 | fn try_to_bson(&self, decoder: &dyn DataDecoder) -> crate::Result { 79 | let mut arr = Vec::with_capacity(self.len()); 80 | for row in self { 81 | let mut m = rbson::Document::new(); 82 | let columns = row.columns(); 83 | for x in columns { 84 | let key = x.name(); 85 | let v: MssqlValueRef = row.try_get_raw(key)?; 86 | let mut bson = v.try_to_bson()?; 87 | decoder.decode(key, &mut bson)?; 88 | m.insert(key.to_owned(), bson); 89 | } 90 | arr.push(Bson::Document(m)); 91 | } 92 | Ok(Bson::from(arr)) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /mybatis-util/src/string_util.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{BTreeMap, HashSet, LinkedList}; 2 | use std::io::Read; 3 | 4 | use rbson::Bson; 5 | 6 | pub const LOG_SPACE: &'static str = " "; 7 | 8 | //find like #{*,*},${*,*} value * 9 | pub fn find_convert_string(arg: &str) -> LinkedList<(String, String)> { 10 | let mut list = LinkedList::new(); 11 | let mut cache = HashSet::new(); 12 | let chars: Vec = arg.bytes().collect(); 13 | let mut item = String::with_capacity(arg.len()); 14 | let mut index: i32 = -1; 15 | for v in &chars { 16 | index = index + 1; 17 | if !item.is_empty() { 18 | item.push(*v as char); 19 | if *v == '}' as u8 { 20 | if cache.get(&item).is_some() { 21 | item.clear(); 22 | continue; 23 | } 24 | let key = item[2..item.len() - 1].to_string(); 25 | cache.insert(item.clone()); 26 | list.push_back((key, item.clone())); 27 | item.clear(); 28 | } 29 | continue; 30 | } 31 | if (*v == '#' as u8 || *v == '$' as u8) 32 | && chars.get(index as usize + 1).eq(&Some(&('{' as u8))) 33 | { 34 | item.push(*v as char); 35 | } 36 | } 37 | return list; 38 | } 39 | 40 | pub fn count_string_num(s: &String, c: char) -> usize { 41 | let cs = s.chars(); 42 | let mut num = 0; 43 | for x in cs { 44 | if x == c { 45 | num += 1; 46 | } 47 | } 48 | return num; 49 | } 50 | 51 | pub fn to_snake_name(name: &str) -> String { 52 | let chs = name.chars(); 53 | let mut new_name = String::new(); 54 | let mut index = 0; 55 | let chs_len = name.len(); 56 | for x in chs { 57 | if x.is_uppercase() { 58 | if index != 0 && (index + 1) != chs_len { 59 | new_name.push_str("_"); 60 | } 61 | new_name.push_str(x.to_lowercase().to_string().as_str()); 62 | } else { 63 | new_name.push(x); 64 | } 65 | index += 1; 66 | } 67 | return new_name; 68 | } 69 | 70 | ///input 'strings' => strings 71 | pub fn un_packing_string(column: &str) -> &str { 72 | if column.len() >= 2 { 73 | if column.starts_with("'") && column.ends_with("'") { 74 | return &column[1..column.len() - 1]; 75 | } 76 | if column.starts_with("`") && column.ends_with("`") { 77 | return &column[1..column.len() - 1]; 78 | } 79 | if column.starts_with("\"") && column.ends_with("\"") { 80 | return &column[1..column.len() - 1]; 81 | } 82 | } 83 | return column; 84 | } 85 | 86 | //find like {*},{*} value * 87 | pub fn find_format_string(arg: &str) -> LinkedList<(String, String)> { 88 | let mut list = LinkedList::new(); 89 | let chars: Vec = arg.bytes().collect(); 90 | let mut item = String::with_capacity(arg.len()); 91 | let mut index: i32 = -1; 92 | for v in &chars { 93 | index = index + 1; 94 | if !item.is_empty() { 95 | item.push(*v as char); 96 | if *v == '}' as u8 { 97 | let key = item[1..item.len() - 1].to_string(); 98 | list.push_back((key, item.clone())); 99 | item.clear(); 100 | } 101 | continue; 102 | } 103 | if *v == '{' as u8 { 104 | item.push(*v as char); 105 | } 106 | } 107 | return list; 108 | } 109 | 110 | #[test] 111 | fn test_find_formats() { 112 | println!("{:?}", find_format_string("1111{}222{1}2")); 113 | } 114 | -------------------------------------------------------------------------------- /mybatis-core/src/types/date_utc.rs: -------------------------------------------------------------------------------- 1 | use chrono::Utc; 2 | use rbson::spec::BinarySubtype; 3 | use rbson::Bson; 4 | use serde::de::Error; 5 | use serde::{Deserializer, Serializer}; 6 | use std::any::type_name; 7 | use std::ops::{Deref, DerefMut}; 8 | use std::str::FromStr; 9 | 10 | #[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] 11 | pub struct DateUtc { 12 | pub inner: chrono::NaiveDate, 13 | } 14 | 15 | impl From for DateUtc { 16 | fn from(arg: chrono::NaiveDate) -> Self { 17 | DateUtc { inner: arg } 18 | } 19 | } 20 | 21 | impl From<&chrono::NaiveDate> for DateUtc { 22 | fn from(arg: &chrono::NaiveDate) -> Self { 23 | DateUtc { inner: arg.clone() } 24 | } 25 | } 26 | 27 | impl serde::Serialize for DateUtc { 28 | #[inline] 29 | fn serialize(&self, serializer: S) -> Result 30 | where 31 | S: Serializer, 32 | { 33 | use serde::ser::Error; 34 | if type_name::().eq("rbson::ser::error::Error") { 35 | return serializer.serialize_str(&format!("DateUtc({})", self.inner)); 36 | } else { 37 | return self.inner.serialize(serializer); 38 | } 39 | } 40 | } 41 | 42 | impl<'de> serde::Deserialize<'de> for DateUtc { 43 | #[inline] 44 | fn deserialize(deserializer: D) -> Result 45 | where 46 | D: Deserializer<'de>, 47 | { 48 | match Bson::deserialize(deserializer)? { 49 | Bson::String(s) => { 50 | if s.starts_with("DateUtc(") && s.ends_with(")") { 51 | let inner_data = &s["DateUtc(".len()..(s.len() - 1)]; 52 | return Ok(Self { 53 | inner: chrono::NaiveDate::from_str(inner_data) 54 | .or_else(|e| Err(D::Error::custom(e.to_string())))?, 55 | }); 56 | } else { 57 | return Ok(Self { 58 | inner: chrono::NaiveDate::from_str(&s) 59 | .or_else(|e| Err(D::Error::custom(e.to_string())))?, 60 | }); 61 | } 62 | } 63 | _ => Err(D::Error::custom("deserialize un supported bson type!")), 64 | } 65 | } 66 | } 67 | 68 | impl std::fmt::Display for DateUtc { 69 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 70 | self.inner.fmt(f) 71 | } 72 | } 73 | 74 | impl std::fmt::Debug for DateUtc { 75 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 76 | self.inner.fmt(f) 77 | } 78 | } 79 | 80 | impl Deref for DateUtc { 81 | type Target = chrono::NaiveDate; 82 | 83 | fn deref(&self) -> &Self::Target { 84 | &self.inner 85 | } 86 | } 87 | 88 | impl DerefMut for DateUtc { 89 | fn deref_mut(&mut self) -> &mut Self::Target { 90 | &mut self.inner 91 | } 92 | } 93 | 94 | impl DateUtc { 95 | /// Returns a [`DateTime`] which corresponds to the current date and time. 96 | pub fn now() -> DateUtc { 97 | let utc = Utc::now(); 98 | Self { 99 | inner: utc.date().naive_local(), 100 | } 101 | } 102 | 103 | /// create from str 104 | pub fn from_str(arg: &str) -> Result { 105 | let inner = chrono::NaiveDate::from_str(arg)?; 106 | Ok(Self { inner: inner }) 107 | } 108 | } 109 | 110 | #[cfg(test)] 111 | mod test { 112 | use crate::types::DateUtc; 113 | 114 | #[test] 115 | fn test_ser_de() { 116 | let b = DateUtc::now(); 117 | let bsons = rbson::to_bson(&b).unwrap(); 118 | let b_de: DateUtc = rbson::from_bson(bsons).unwrap(); 119 | assert_eq!(b, b_de); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /mybatis-core/src/types/time_utc.rs: -------------------------------------------------------------------------------- 1 | use chrono::Utc; 2 | use rbson::spec::BinarySubtype; 3 | use rbson::Bson; 4 | use serde::de::Error; 5 | use serde::{Deserializer, Serializer}; 6 | use std::any::type_name; 7 | use std::ops::{Deref, DerefMut}; 8 | use std::str::FromStr; 9 | 10 | #[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] 11 | pub struct TimeUtc { 12 | pub inner: chrono::NaiveTime, 13 | } 14 | 15 | impl From for TimeUtc { 16 | fn from(arg: chrono::NaiveTime) -> Self { 17 | Self { inner: arg } 18 | } 19 | } 20 | 21 | impl From<&chrono::NaiveTime> for TimeUtc { 22 | fn from(arg: &chrono::NaiveTime) -> Self { 23 | Self { inner: arg.clone() } 24 | } 25 | } 26 | 27 | impl serde::Serialize for TimeUtc { 28 | #[inline] 29 | fn serialize(&self, serializer: S) -> Result 30 | where 31 | S: Serializer, 32 | { 33 | use serde::ser::Error; 34 | if type_name::().eq("rbson::ser::error::Error") { 35 | return serializer.serialize_str(&format!("TimeUtc({})", self.inner)); 36 | } else { 37 | return self.inner.serialize(serializer); 38 | } 39 | } 40 | } 41 | 42 | impl<'de> serde::Deserialize<'de> for TimeUtc { 43 | #[inline] 44 | fn deserialize(deserializer: D) -> Result 45 | where 46 | D: Deserializer<'de>, 47 | { 48 | match Bson::deserialize(deserializer)? { 49 | Bson::String(s) => { 50 | if s.starts_with("TimeUtc(") && s.ends_with(")") { 51 | let inner_data = &s["TimeUtc(".len()..(s.len() - 1)]; 52 | return Ok(Self { 53 | inner: chrono::NaiveTime::from_str(inner_data) 54 | .or_else(|e| Err(D::Error::custom(e.to_string())))?, 55 | }); 56 | } else { 57 | return Ok(Self { 58 | inner: chrono::NaiveTime::from_str(&s) 59 | .or_else(|e| Err(D::Error::custom(e.to_string())))?, 60 | }); 61 | } 62 | } 63 | _ => Err(D::Error::custom("deserialize un supported bson type!")), 64 | } 65 | } 66 | } 67 | 68 | impl std::fmt::Display for TimeUtc { 69 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 70 | self.inner.fmt(f) 71 | } 72 | } 73 | 74 | impl std::fmt::Debug for TimeUtc { 75 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 76 | self.inner.fmt(f) 77 | } 78 | } 79 | 80 | impl Deref for TimeUtc { 81 | type Target = chrono::NaiveTime; 82 | 83 | fn deref(&self) -> &Self::Target { 84 | &self.inner 85 | } 86 | } 87 | 88 | impl DerefMut for TimeUtc { 89 | fn deref_mut(&mut self) -> &mut Self::Target { 90 | &mut self.inner 91 | } 92 | } 93 | 94 | impl TimeUtc { 95 | /// Returns a [`DateTime`] which corresponds to the current date and time. 96 | pub fn now() -> TimeUtc { 97 | let utc = Utc::now(); 98 | let dt = rbson::DateTime::from_millis(utc.timestamp_millis()); 99 | Self { 100 | inner: dt.to_chrono().with_timezone(&Utc).time(), 101 | } 102 | } 103 | 104 | /// create from str 105 | pub fn from_str(arg: &str) -> Result { 106 | let inner = chrono::NaiveTime::from_str(arg)?; 107 | Ok(Self { inner: inner }) 108 | } 109 | } 110 | 111 | #[cfg(test)] 112 | mod test { 113 | use crate::types::TimeUtc; 114 | 115 | #[test] 116 | fn test_ser_de() { 117 | let b = TimeUtc::now(); 118 | let bsons = rbson::to_bson(&b).unwrap(); 119 | let b_de: TimeUtc = rbson::from_bson(bsons).unwrap(); 120 | assert_eq!(b, b_de); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /mybatis-core/src/sqlite/sqlx_value.rs: -------------------------------------------------------------------------------- 1 | use chrono::Utc; 2 | use rbson::spec::BinarySubtype; 3 | use rbson::{bson, to_bson, Bson}; 4 | use sqlx_core::column::Column; 5 | use sqlx_core::decode::Decode; 6 | use sqlx_core::error::BoxDynError; 7 | use sqlx_core::row::Row; 8 | use sqlx_core::sqlite::SqliteRow; 9 | use sqlx_core::sqlite::{Sqlite, SqliteValue, SqliteValueRef}; 10 | use sqlx_core::type_info::TypeInfo; 11 | use sqlx_core::value::ValueRef; 12 | 13 | use crate::convert::{JsonCodec, RefJsonCodec, ResultCodec}; 14 | use crate::db::db_adapter::DataDecoder; 15 | use crate::to_bson_macro; 16 | 17 | impl<'c> JsonCodec for SqliteValueRef<'c> { 18 | fn try_to_bson(self) -> crate::Result { 19 | let type_string = self.type_info().name().to_owned(); 20 | return match type_string.as_str() { 21 | "NULL" => Ok(Bson::Null), 22 | "TEXT" => { 23 | let r: Option = Decode::<'_, Sqlite>::decode(self)?; 24 | return Ok(to_bson_macro!(r)); 25 | } 26 | "BOOLEAN" => { 27 | let r: Option = Decode::<'_, Sqlite>::decode(self)?; 28 | return Ok(to_bson_macro!(r)); 29 | } 30 | "INTEGER" => { 31 | let r: Option = Decode::<'_, Sqlite>::decode(self)?; 32 | return Ok(to_bson_macro!(r)); 33 | } 34 | "REAL" => { 35 | let r: Option = Decode::<'_, Sqlite>::decode(self)?; 36 | return Ok(to_bson_macro!(r)); 37 | } 38 | "BLOB" => { 39 | let r: Option> = Decode::<'_, Sqlite>::decode(self)?; 40 | if let Some(r) = r { 41 | return Ok(Bson::Binary(rbson::Binary { 42 | subtype: BinarySubtype::Generic, 43 | bytes: r, 44 | })); 45 | } 46 | return Ok(Bson::Null); 47 | } 48 | "DATE" => { 49 | let r: Option = Decode::<'_, Sqlite>::decode(self)?; 50 | return Ok(to_bson(&r).unwrap_or_default()); 51 | } 52 | "TIME" => { 53 | let r: Option = Decode::<'_, Sqlite>::decode(self)?; 54 | return Ok(to_bson(&r).unwrap_or_default()); 55 | } 56 | "DATETIME" => { 57 | let r: Option = Decode::<'_, Sqlite>::decode(self)?; 58 | if let Some(dt) = r { 59 | return Ok(Bson::String(dt.format("%Y-%m-%dT%H:%M:%S").to_string())); 60 | } 61 | return Ok(Bson::Null); 62 | } 63 | _ => { 64 | //TODO "NUMERIC" 65 | //you can use already supported types to decode this 66 | let r: Option> = Decode::<'_, Sqlite>::decode(self)?; 67 | if let Some(r) = r { 68 | return Ok(Bson::Binary(rbson::Binary { 69 | subtype: BinarySubtype::Generic, 70 | bytes: r, 71 | })); 72 | } 73 | return Ok(Bson::Null); 74 | } 75 | }; 76 | } 77 | } 78 | 79 | impl RefJsonCodec for Vec { 80 | fn try_to_bson(&self, decoder: &dyn DataDecoder) -> crate::Result { 81 | let mut arr = Vec::with_capacity(self.len()); 82 | for row in self { 83 | let mut m = rbson::Document::new(); 84 | let columns = row.columns(); 85 | for x in columns { 86 | let key = x.name(); 87 | let v: SqliteValueRef = row.try_get_raw(key)?; 88 | let mut bson = v.try_to_bson()?; 89 | decoder.decode(key, &mut bson)?; 90 | m.insert(key.to_owned(), bson); 91 | } 92 | arr.push(Bson::Document(m)); 93 | } 94 | Ok(Bson::from(arr)) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /mybatis-core/src/types/date_native.rs: -------------------------------------------------------------------------------- 1 | use chrono::{Local, NaiveDate}; 2 | use rbson::spec::BinarySubtype; 3 | use rbson::Bson; 4 | use serde::de::Error; 5 | use serde::{Deserializer, Serializer}; 6 | use std::any::type_name; 7 | use std::ops::{Deref, DerefMut}; 8 | use std::str::FromStr; 9 | 10 | /// Rust type Postgres type(s) 11 | /// chrono::NaiveDate DATE 12 | #[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] 13 | pub struct DateNative { 14 | pub inner: chrono::NaiveDate, 15 | } 16 | 17 | impl From for DateNative { 18 | fn from(arg: chrono::NaiveDate) -> Self { 19 | DateNative { inner: arg } 20 | } 21 | } 22 | 23 | impl From<&chrono::NaiveDate> for DateNative { 24 | fn from(arg: &chrono::NaiveDate) -> Self { 25 | DateNative { inner: arg.clone() } 26 | } 27 | } 28 | 29 | impl serde::Serialize for DateNative { 30 | #[inline] 31 | fn serialize(&self, serializer: S) -> Result 32 | where 33 | S: Serializer, 34 | { 35 | use serde::ser::Error; 36 | if type_name::().eq("rbson::ser::error::Error") { 37 | return serializer.serialize_str(&format!("DateNative({})", self.inner)); 38 | } else { 39 | return self.inner.serialize(serializer); 40 | } 41 | } 42 | } 43 | 44 | impl<'de> serde::Deserialize<'de> for DateNative { 45 | #[inline] 46 | fn deserialize(deserializer: D) -> Result 47 | where 48 | D: Deserializer<'de>, 49 | { 50 | match Bson::deserialize(deserializer)? { 51 | Bson::String(s) => { 52 | if s.starts_with("DateNative(") && s.ends_with(")") { 53 | let inner_data = &s["DateNative(".len()..(s.len() - 1)]; 54 | return Ok(Self { 55 | inner: chrono::NaiveDate::from_str(inner_data) 56 | .or_else(|e| Err(D::Error::custom(e.to_string())))?, 57 | }); 58 | } else { 59 | return Ok(Self { 60 | inner: chrono::NaiveDate::from_str(&s) 61 | .or_else(|e| Err(D::Error::custom(e.to_string())))?, 62 | }); 63 | } 64 | } 65 | _ => Err(D::Error::custom("deserialize un supported bson type!")), 66 | } 67 | } 68 | } 69 | 70 | impl std::fmt::Display for DateNative { 71 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 72 | self.inner.fmt(f) 73 | } 74 | } 75 | 76 | impl std::fmt::Debug for DateNative { 77 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 78 | self.inner.fmt(f) 79 | } 80 | } 81 | 82 | impl Deref for DateNative { 83 | type Target = chrono::NaiveDate; 84 | 85 | fn deref(&self) -> &Self::Target { 86 | &self.inner 87 | } 88 | } 89 | 90 | impl DerefMut for DateNative { 91 | fn deref_mut(&mut self) -> &mut Self::Target { 92 | &mut self.inner 93 | } 94 | } 95 | 96 | impl DateNative { 97 | /// Returns a [`DateTime`] which corresponds to the current date and time. 98 | pub fn now() -> DateNative { 99 | let utc = Local::now(); 100 | Self { 101 | inner: utc.date().naive_local(), 102 | } 103 | } 104 | 105 | /// create from str 106 | pub fn from_str(arg: &str) -> Result { 107 | let inner = chrono::NaiveDate::from_str(arg)?; 108 | Ok(Self { inner: inner }) 109 | } 110 | } 111 | 112 | #[cfg(test)] 113 | mod test { 114 | use crate::types::DateNative; 115 | 116 | #[test] 117 | fn test_ser_de() { 118 | let b = DateNative::now(); 119 | let bsons = rbson::to_bson(&b).unwrap(); 120 | let b_de: DateNative = rbson::from_bson(bsons).unwrap(); 121 | assert_eq!(b, b_de); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /mybatis/src/logic_delete.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Debug, Display, Formatter}; 2 | 3 | use rbson::Bson; 4 | 5 | use crate::plus::{MybatisPlus, Skip}; 6 | use mybatis_core::db::DriverType; 7 | use mybatis_core::Error; 8 | use mybatis_sql::rule::SqlRule; 9 | use mybatis_sql::TEMPLATE; 10 | use serde::de::DeserializeOwned; 11 | use serde::{Deserialize, Deserializer, Serialize, Serializer}; 12 | use std::collections::HashMap; 13 | use std::marker::PhantomData; 14 | use std::ops::{Deref, DerefMut}; 15 | 16 | /// Logic Delete Plugin trait 17 | pub trait LogicDelete: Send + Sync + Debug { 18 | /// database column 19 | fn column(&self) -> &str; 20 | /// deleted data,must be i32 21 | fn deleted(&self) -> i32; 22 | /// un deleted data,must be i32 23 | fn un_deleted(&self) -> i32; 24 | /// create_remove_sql 25 | fn create_remove_sql( 26 | &self, 27 | driver_type: &DriverType, 28 | table_name: &str, 29 | table_fields: &str, 30 | sql_where: &str, 31 | ) -> Result; 32 | } 33 | 34 | pub struct MyBatisLogicDeletePlugin { 35 | pub column: String, 36 | pub deleted: i32, 37 | pub un_deleted: i32, 38 | } 39 | 40 | impl MyBatisLogicDeletePlugin { 41 | pub fn new(column: &str) -> Self { 42 | Self { 43 | column: column.to_string(), 44 | deleted: 1, 45 | un_deleted: 0, 46 | } 47 | } 48 | 49 | pub fn new_opt(column: &str, deleted: i32, un_deleted: i32) -> Self { 50 | if deleted == un_deleted { 51 | panic!("[rbaits] deleted can not equal to un_deleted on MyBatisLogicDeletePlugin::new_opt(column: &str, deleted: i32, un_deleted: i32)") 52 | } 53 | Self { 54 | column: column.to_string(), 55 | deleted, 56 | un_deleted, 57 | } 58 | } 59 | } 60 | 61 | impl Debug for MyBatisLogicDeletePlugin { 62 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 63 | f.debug_struct("MyBatisLogicDeletePlugin").finish() 64 | } 65 | } 66 | 67 | impl LogicDelete for MyBatisLogicDeletePlugin { 68 | fn column(&self) -> &str { 69 | self.column.as_str() 70 | } 71 | 72 | fn deleted(&self) -> i32 { 73 | self.deleted 74 | } 75 | 76 | fn un_deleted(&self) -> i32 { 77 | self.un_deleted 78 | } 79 | 80 | fn create_remove_sql( 81 | &self, 82 | driver_type: &DriverType, 83 | table_name: &str, 84 | table_fields: &str, 85 | sql_where: &str, 86 | ) -> Result { 87 | let fields: Vec<&str> = table_fields.split(",").collect(); 88 | return if fields.contains(&self.column()) { 89 | //fields have column 90 | if sql_where.is_empty() { 91 | let new_sql = format!( 92 | "{} {} {} {} = {}", 93 | TEMPLATE.update.value, 94 | table_name, 95 | TEMPLATE.set.value, 96 | self.column(), 97 | self.deleted() 98 | ) + sql_where; 99 | Ok(new_sql) 100 | } else { 101 | let new_sql = format!( 102 | "{} {} {} {} = {} {}", 103 | TEMPLATE.update.value, 104 | table_name, 105 | TEMPLATE.set.value, 106 | self.column(), 107 | self.deleted(), 108 | sql_where.trim_start() 109 | ); 110 | Ok(new_sql) 111 | } 112 | } else if !sql_where.is_empty() { 113 | let new_sql = format!( 114 | "{} {} {}", 115 | TEMPLATE.delete_from.value, 116 | table_name, 117 | sql_where.trim_start() 118 | ); 119 | Ok(new_sql) 120 | } else { 121 | Err(Error::from("[mybatis] del data must have where sql!")) 122 | }; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /mybatis-core/src/types/time_native.rs: -------------------------------------------------------------------------------- 1 | use chrono::{Local, Utc}; 2 | use rbson::spec::BinarySubtype; 3 | use rbson::Bson; 4 | use serde::de::Error; 5 | use serde::{Deserializer, Serializer}; 6 | use std::any::type_name; 7 | use std::ops::{Add, Deref, DerefMut}; 8 | use std::str::FromStr; 9 | use std::time::SystemTime; 10 | 11 | /// TimeLocal 12 | /// Rust type Postgres type(s) 13 | /// chrono::NaiveTime TIME 14 | #[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] 15 | pub struct TimeNative { 16 | pub inner: chrono::NaiveTime, 17 | } 18 | 19 | impl From for TimeNative { 20 | fn from(arg: chrono::NaiveTime) -> Self { 21 | Self { inner: arg } 22 | } 23 | } 24 | 25 | impl From<&chrono::NaiveTime> for TimeNative { 26 | fn from(arg: &chrono::NaiveTime) -> Self { 27 | Self { inner: arg.clone() } 28 | } 29 | } 30 | 31 | impl serde::Serialize for TimeNative { 32 | #[inline] 33 | fn serialize(&self, serializer: S) -> Result 34 | where 35 | S: Serializer, 36 | { 37 | use serde::ser::Error; 38 | if type_name::().eq("rbson::ser::error::Error") { 39 | return serializer.serialize_str(&format!("TimeNative({})", self.inner)); 40 | } else { 41 | return self.inner.serialize(serializer); 42 | } 43 | } 44 | } 45 | 46 | impl<'de> serde::Deserialize<'de> for TimeNative { 47 | #[inline] 48 | fn deserialize(deserializer: D) -> Result 49 | where 50 | D: Deserializer<'de>, 51 | { 52 | match Bson::deserialize(deserializer)? { 53 | Bson::String(s) => { 54 | if s.starts_with("TimeNative(") && s.ends_with(")") { 55 | let inner_data = &s["TimeNative(".len()..(s.len() - 1)]; 56 | return Ok(Self { 57 | inner: chrono::NaiveTime::from_str(inner_data) 58 | .or_else(|e| Err(D::Error::custom(e.to_string())))?, 59 | }); 60 | } else { 61 | return Ok(Self { 62 | inner: chrono::NaiveTime::from_str(&s) 63 | .or_else(|e| Err(D::Error::custom(e.to_string())))?, 64 | }); 65 | } 66 | } 67 | _ => Err(D::Error::custom("deserialize un supported bson type!")), 68 | } 69 | } 70 | } 71 | 72 | impl std::fmt::Display for TimeNative { 73 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 74 | self.inner.fmt(f) 75 | } 76 | } 77 | 78 | impl std::fmt::Debug for TimeNative { 79 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 80 | self.inner.fmt(f) 81 | } 82 | } 83 | 84 | impl Deref for TimeNative { 85 | type Target = chrono::NaiveTime; 86 | 87 | fn deref(&self) -> &Self::Target { 88 | &self.inner 89 | } 90 | } 91 | 92 | impl DerefMut for TimeNative { 93 | fn deref_mut(&mut self) -> &mut Self::Target { 94 | &mut self.inner 95 | } 96 | } 97 | 98 | impl TimeNative { 99 | /// Returns a [`DateTime`] which corresponds to the current date and time. 100 | pub fn now() -> TimeNative { 101 | let utc = Local::now(); 102 | let dt = rbson::DateTime::from_millis(utc.timestamp_millis()); 103 | Self { 104 | inner: dt.to_chrono().with_timezone(&Local).time(), 105 | } 106 | } 107 | 108 | /// create from str 109 | pub fn from_str(arg: &str) -> Result { 110 | let inner = chrono::NaiveTime::from_str(arg)?; 111 | Ok(Self { inner: inner }) 112 | } 113 | } 114 | 115 | #[cfg(test)] 116 | mod test { 117 | use crate::types::TimeNative; 118 | 119 | #[test] 120 | fn test_ser_de() { 121 | let b = TimeNative::now(); 122 | let bsons = rbson::to_bson(&b).unwrap(); 123 | let b_de: TimeNative = rbson::from_bson(bsons).unwrap(); 124 | assert_eq!(b, b_de); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /mybatis-core/src/db/bind_mssql.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Error; 2 | use crate::types::{DateNative, DateTimeUtc, DateUtc, Decimal, TimeNative, TimeUtc}; 3 | use crate::{DateTimeNative, Uuid}; 4 | use rbson::spec::BinarySubtype; 5 | use rbson::Bson; 6 | use sqlx_core::mssql::{Mssql, MssqlArguments}; 7 | use sqlx_core::query::Query; 8 | 9 | #[inline] 10 | pub fn bind( 11 | t: Bson, 12 | mut q: Query, 13 | ) -> crate::Result> { 14 | match t { 15 | Bson::String(s) => { 16 | if s.starts_with("DateTimeUtc(") { 17 | let data: DateTimeUtc = rbson::from_bson(Bson::String(s))?; 18 | q = q.bind(data.inner.to_string()); 19 | return Ok(q); 20 | } 21 | if s.starts_with("DateTimeNative(") { 22 | let data: DateTimeNative = rbson::from_bson(Bson::String(s))?; 23 | q = q.bind(data.inner.to_string()); 24 | return Ok(q); 25 | } 26 | if s.starts_with("DateNative(") { 27 | let data: DateNative = rbson::from_bson(Bson::String(s))?; 28 | q = q.bind(data.inner.to_string()); 29 | return Ok(q); 30 | } 31 | if s.starts_with("DateUtc(") { 32 | let data: DateUtc = rbson::from_bson(Bson::String(s))?; 33 | q = q.bind(data.inner.to_string()); 34 | return Ok(q); 35 | } 36 | if s.starts_with("TimeUtc(") { 37 | let data: TimeUtc = rbson::from_bson(Bson::String(s))?; 38 | q = q.bind(data.inner.to_string()); 39 | return Ok(q); 40 | } 41 | if s.starts_with("TimeNative(") { 42 | let data: TimeNative = rbson::from_bson(Bson::String(s))?; 43 | q = q.bind(data.inner.to_string()); 44 | return Ok(q); 45 | } 46 | if s.starts_with("Decimal(") { 47 | let data: Decimal = rbson::from_bson(Bson::String(s))?; 48 | q = q.bind(data.inner.to_string()); 49 | return Ok(q); 50 | } 51 | if s.starts_with("Uuid(") { 52 | let data: Uuid = rbson::from_bson(Bson::String(s))?; 53 | q = q.bind(data.inner.to_string()); 54 | return Ok(q); 55 | } 56 | q = q.bind(Some(s)); 57 | } 58 | Bson::Null => { 59 | q = q.bind(Option::::None); 60 | } 61 | Bson::Int32(n) => { 62 | q = q.bind(n); 63 | } 64 | Bson::Int64(n) => { 65 | q = q.bind(n); 66 | } 67 | Bson::UInt32(n) => { 68 | q = q.bind(n as i32); 69 | } 70 | Bson::UInt64(n) => { 71 | q = q.bind(n as i64); 72 | } 73 | Bson::Double(n) => { 74 | q = q.bind(n); 75 | } 76 | Bson::Boolean(b) => { 77 | q = q.bind(b); 78 | } 79 | Bson::Binary(d) => match d.subtype { 80 | BinarySubtype::Uuid => { 81 | q = q.bind(crate::types::Uuid::from(d).to_string()); 82 | } 83 | BinarySubtype::UserDefined(type_id) => match type_id { 84 | crate::types::BINARY_SUBTYPE_JSON => { 85 | q = q.bind(String::from_utf8(d.bytes).unwrap_or_default()); 86 | } 87 | _ => { 88 | return Err(Error::from("un supported bind type!")); 89 | } 90 | }, 91 | _ => { 92 | return Err(Error::from("un supported bind type!")); 93 | } 94 | }, 95 | Bson::Decimal128(d) => { 96 | q = q.bind(d.to_string()); 97 | } 98 | Bson::DateTime(d) => { 99 | q = q.bind(d.to_string()); 100 | } 101 | Bson::Timestamp(d) => { 102 | q = q.bind(crate::types::Timestamp::from(d).inner.to_string()); 103 | } 104 | _ => { 105 | return crate::Result::Err(crate::Error::from("unsupported type!")); 106 | } 107 | } 108 | return Ok(q); 109 | } 110 | -------------------------------------------------------------------------------- /mybatis-core/src/db/bind_sqlite.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Error; 2 | use crate::types::{ 3 | DateNative, DateTimeNative, DateTimeUtc, DateUtc, Decimal, TimeNative, TimeUtc, 4 | }; 5 | use crate::Uuid; 6 | use rbson::spec::BinarySubtype; 7 | use rbson::Bson; 8 | use sqlx_core::query::Query; 9 | use sqlx_core::sqlite::{Sqlite, SqliteArguments}; 10 | 11 | #[inline] 12 | pub fn bind<'a>( 13 | t: Bson, 14 | mut q: Query<'a, Sqlite, SqliteArguments<'a>>, 15 | ) -> crate::Result>> { 16 | match t { 17 | Bson::String(s) => { 18 | if s.starts_with("DateTimeUtc(") { 19 | let data: DateTimeUtc = rbson::from_bson(Bson::String(s))?; 20 | q = q.bind(data.inner); 21 | return Ok(q); 22 | } 23 | if s.starts_with("DateTimeNative(") { 24 | let data: DateTimeNative = rbson::from_bson(Bson::String(s))?; 25 | q = q.bind(data.inner); 26 | return Ok(q); 27 | } 28 | if s.starts_with("DateNative(") { 29 | let data: DateNative = rbson::from_bson(Bson::String(s))?; 30 | q = q.bind(data.inner); 31 | return Ok(q); 32 | } 33 | if s.starts_with("DateUtc(") { 34 | let data: DateUtc = rbson::from_bson(Bson::String(s))?; 35 | q = q.bind(data.inner); 36 | return Ok(q); 37 | } 38 | if s.starts_with("TimeUtc(") { 39 | let data: TimeUtc = rbson::from_bson(Bson::String(s))?; 40 | q = q.bind(data.inner); 41 | return Ok(q); 42 | } 43 | if s.starts_with("TimeNative(") { 44 | let data: TimeNative = rbson::from_bson(Bson::String(s))?; 45 | q = q.bind(data.inner); 46 | return Ok(q); 47 | } 48 | if s.starts_with("Decimal(") { 49 | let data: Decimal = rbson::from_bson(Bson::String(s))?; 50 | q = q.bind(data.inner.to_string()); 51 | return Ok(q); 52 | } 53 | if s.starts_with("Uuid(") { 54 | let data: Uuid = rbson::from_bson(Bson::String(s))?; 55 | q = q.bind(data.inner.to_string()); 56 | return Ok(q); 57 | } 58 | q = q.bind(Some(s)); 59 | } 60 | Bson::Null => { 61 | q = q.bind(Option::::None); 62 | } 63 | Bson::Int32(n) => { 64 | q = q.bind(n); 65 | } 66 | Bson::Int64(n) => { 67 | q = q.bind(n); 68 | } 69 | Bson::UInt32(n) => { 70 | q = q.bind(n); 71 | } 72 | Bson::UInt64(n) => { 73 | q = q.bind(n as i64); 74 | } 75 | Bson::Double(n) => { 76 | q = q.bind(n); 77 | } 78 | Bson::Boolean(b) => { 79 | q = q.bind(b); 80 | } 81 | Bson::Decimal128(d) => { 82 | q = q.bind(d.to_string()); 83 | } 84 | Bson::Binary(d) => match d.subtype { 85 | BinarySubtype::Generic => { 86 | q = q.bind(d.bytes); 87 | } 88 | BinarySubtype::Uuid => { 89 | q = q.bind(crate::types::Uuid::from(d).to_string()); 90 | } 91 | BinarySubtype::UserDefined(type_id) => match type_id { 92 | crate::types::BINARY_SUBTYPE_JSON => { 93 | q = q.bind(d.bytes); 94 | } 95 | _ => { 96 | return Err(Error::from("un supported bind type!")); 97 | } 98 | }, 99 | _ => { 100 | return Err(Error::from("un supported bind type!")); 101 | } 102 | }, 103 | Bson::DateTime(d) => { 104 | q = q.bind(DateTimeNative::from(d).inner); 105 | } 106 | Bson::Timestamp(d) => { 107 | q = q.bind(crate::types::Timestamp::from(d).inner.to_string()); 108 | } 109 | _ => { 110 | return crate::Result::Err(crate::Error::from("unsupported type!")); 111 | } 112 | } 113 | return Ok(q); 114 | } 115 | -------------------------------------------------------------------------------- /mybatis-macro/src/macros/mybatis_html_impl.rs: -------------------------------------------------------------------------------- 1 | use quote::quote; 2 | use quote::ToTokens; 3 | use syn::{AttributeArgs, FnArg, ItemFn}; 4 | 5 | use crate::macros::py_sql_impl; 6 | use crate::proc_macro::TokenStream; 7 | use crate::util::{ 8 | find_fn_body, find_return_type, get_fn_args, get_page_req_ident, is_fetch, is_mybatis_ref, 9 | }; 10 | 11 | pub(crate) fn impl_macro_mybatis_html(target_fn: &ItemFn, args: &AttributeArgs) -> TokenStream { 12 | let return_ty = find_return_type(target_fn); 13 | let func_name_ident = target_fn.sig.ident.to_token_stream(); 14 | 15 | let mut mybatis_ident = "".to_token_stream(); 16 | let mut mybatis_name = String::new(); 17 | for x in &target_fn.sig.inputs { 18 | match x { 19 | FnArg::Receiver(_) => {} 20 | FnArg::Typed(t) => { 21 | let ty_stream = t.ty.to_token_stream().to_string(); 22 | if is_mybatis_ref(&ty_stream) { 23 | mybatis_ident = t.pat.to_token_stream(); 24 | mybatis_name = mybatis_ident.to_string(); 25 | break; 26 | } 27 | } 28 | } 29 | } 30 | let sql_ident; 31 | if args.len() == 1 { 32 | if mybatis_name.is_empty() { 33 | panic!("[mybatis] you should add mybatis ref param rb:&Mybatis or rb: &mut MybatisExecutor<'_,'_> on '{}()'!", target_fn.sig.ident); 34 | } 35 | sql_ident = args 36 | .get(0) 37 | .expect("[mybatis] miss htmlsql sql param!") 38 | .to_token_stream(); 39 | } else if args.len() == 2 { 40 | mybatis_ident = args 41 | .get(0) 42 | .expect("[mybatis] miss mybatis ident param!") 43 | .to_token_stream(); 44 | mybatis_name = format!("{}", mybatis_ident); 45 | sql_ident = args 46 | .get(1) 47 | .expect("[mybatis] miss html file name param!") 48 | .to_token_stream(); 49 | } else { 50 | panic!("[mybatis] Incorrect macro parameter length!"); 51 | } 52 | let func_args_stream = target_fn.sig.inputs.to_token_stream(); 53 | let fn_body = find_fn_body(target_fn); 54 | let is_async = target_fn.sig.asyncness.is_some(); 55 | if !is_async { 56 | panic!( 57 | "[mybatis] #[mybatis_plus] 'fn {}({})' must be async fn! ", 58 | func_name_ident, func_args_stream 59 | ); 60 | } 61 | //append all args 62 | let sql_args_gen = py_sql_impl::filter_args_context_id(&mybatis_name, &get_fn_args(target_fn)); 63 | let is_fetch = is_fetch(&return_ty.to_string()); 64 | let mut call_method = quote! {}; 65 | if is_fetch { 66 | call_method = quote! { 67 | use mybatis::executor::{Executor,ExecutorMut}; 68 | #mybatis_ident.fetch(&sql,rb_args).await 69 | }; 70 | } else { 71 | call_method = quote! { 72 | use mybatis::executor::{Executor,ExecutorMut}; 73 | #mybatis_ident.exec(&sql,rb_args).await 74 | }; 75 | } 76 | if return_ty.to_string().contains("Page <") 77 | && func_args_stream.to_string().contains("& PageRequest") 78 | { 79 | let page_ident = get_page_req_ident(target_fn, &func_name_ident.to_string()); 80 | call_method = quote! { 81 | use mybatis::plus::{Mapping,MappingMut}; 82 | #mybatis_ident.fetch_page(&sql,rb_args,#page_ident).await 83 | }; 84 | println!("gen return"); 85 | } 86 | 87 | //gen rust code templete 88 | return quote! { 89 | pub async fn #func_name_ident(#func_args_stream) -> #return_ty { 90 | let mut rb_arg_map = rbson::Document::new(); 91 | #sql_args_gen 92 | #fn_body 93 | use mybatis::executor::{MybatisRef}; 94 | let driver_type = #mybatis_ident.get_mybatis().driver_type()?; 95 | use mybatis::{mybatis_sql,AsSqlTag}; 96 | let sql_tag = driver_type.sql_tag(); 97 | #[rb_html(#sql_ident)] 98 | pub fn #func_name_ident(arg: &rbson::Bson, _tag: char) {} 99 | let (mut sql,rb_args) = #func_name_ident(&rbson::Bson::Document(rb_arg_map),sql_tag); 100 | driver_type.do_replace_tag(&mut sql); 101 | #call_method 102 | } 103 | } 104 | .into(); 105 | } 106 | -------------------------------------------------------------------------------- /mybatis-core/src/db/bind_mysql.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Error; 2 | use crate::types::{ 3 | DateNative, DateTimeNative, DateTimeUtc, DateUtc, Decimal, TimeNative, TimeUtc, 4 | }; 5 | use crate::Uuid; 6 | use rbson::spec::BinarySubtype; 7 | use rbson::Bson; 8 | use sqlx_core::mysql::{MySql, MySqlArguments}; 9 | use sqlx_core::query::Query; 10 | 11 | #[inline] 12 | pub fn bind( 13 | t: Bson, 14 | mut q: Query, 15 | ) -> crate::Result> { 16 | match t { 17 | Bson::String(s) => { 18 | if s.starts_with("DateTimeUtc(") { 19 | let data: DateTimeUtc = rbson::from_bson(Bson::String(s))?; 20 | q = q.bind(data.inner); 21 | return Ok(q); 22 | } 23 | if s.starts_with("DateTimeNative(") { 24 | let data: DateTimeNative = rbson::from_bson(Bson::String(s))?; 25 | q = q.bind(data.inner); 26 | return Ok(q); 27 | } 28 | if s.starts_with("DateNative(") { 29 | let data: DateNative = rbson::from_bson(Bson::String(s))?; 30 | q = q.bind(data.inner); 31 | return Ok(q); 32 | } 33 | if s.starts_with("DateUtc(") { 34 | let data: DateUtc = rbson::from_bson(Bson::String(s))?; 35 | q = q.bind(data.inner); 36 | return Ok(q); 37 | } 38 | if s.starts_with("TimeUtc(") { 39 | let data: TimeUtc = rbson::from_bson(Bson::String(s))?; 40 | q = q.bind(data.inner); 41 | return Ok(q); 42 | } 43 | if s.starts_with("TimeNative(") { 44 | let data: TimeNative = rbson::from_bson(Bson::String(s))?; 45 | q = q.bind(data.inner); 46 | return Ok(q); 47 | } 48 | if s.starts_with("Decimal(") { 49 | let data: Decimal = rbson::from_bson(Bson::String(s))?; 50 | q = q.bind(data.inner.to_string()); 51 | return Ok(q); 52 | } 53 | if s.starts_with("Uuid(") { 54 | let data: Uuid = rbson::from_bson(Bson::String(s))?; 55 | q = q.bind(data.inner.to_string()); 56 | return Ok(q); 57 | } 58 | q = q.bind(Some(s)); 59 | } 60 | Bson::Null => { 61 | q = q.bind(Option::::None); 62 | } 63 | Bson::Int32(n) => { 64 | q = q.bind(n); 65 | } 66 | Bson::Int64(n) => { 67 | q = q.bind(n); 68 | } 69 | Bson::UInt32(n) => { 70 | q = q.bind(n); 71 | } 72 | Bson::UInt64(n) => { 73 | q = q.bind(n); 74 | } 75 | Bson::Double(n) => { 76 | q = q.bind(n); 77 | } 78 | Bson::Boolean(b) => { 79 | q = q.bind(b); 80 | } 81 | Bson::Decimal128(d) => { 82 | q = q.bind(d.to_string()); 83 | } 84 | Bson::Binary(d) => match d.subtype { 85 | BinarySubtype::Generic => { 86 | q = q.bind(d.bytes); 87 | } 88 | BinarySubtype::Uuid => { 89 | q = q.bind(crate::types::Uuid::from(d).inner); 90 | } 91 | BinarySubtype::UserDefined(type_id) => match type_id { 92 | crate::types::BINARY_SUBTYPE_JSON => { 93 | q = q.bind( 94 | serde_json::from_slice::(&d.bytes).unwrap_or_default(), 95 | ); 96 | } 97 | _ => { 98 | return Err(Error::from("un supported bind type!")); 99 | } 100 | }, 101 | _ => { 102 | return Err(Error::from("un supported bind type!")); 103 | } 104 | }, 105 | Bson::DateTime(d) => { 106 | q = q.bind(DateTimeNative::from(d).inner); 107 | } 108 | Bson::Timestamp(d) => { 109 | q = q.bind(crate::types::Timestamp::from(d).inner); 110 | } 111 | _ => { 112 | return crate::Result::Err(crate::Error::from("unsupported type!")); 113 | } 114 | } 115 | return Ok(q); 116 | } 117 | -------------------------------------------------------------------------------- /mybatis-core/src/types/uuids.rs: -------------------------------------------------------------------------------- 1 | use rbson::spec::BinarySubtype; 2 | use rbson::{Binary, Bson}; 3 | use serde::de::Error; 4 | use serde::{Deserializer, Serialize, Serializer}; 5 | use std::any::type_name; 6 | use std::ops::{Deref, DerefMut}; 7 | use std::str::FromStr; 8 | 9 | /// Uuid 10 | #[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] 11 | pub struct Uuid { 12 | pub inner: uuid::Uuid, 13 | } 14 | 15 | impl From<&rbson::Binary> for Uuid { 16 | fn from(arg: &rbson::Binary) -> Self { 17 | let id = &String::from_utf8(arg.bytes.clone()).unwrap_or_default(); 18 | Uuid { 19 | inner: uuid::Uuid::from_str(&id).unwrap_or_default(), 20 | } 21 | } 22 | } 23 | 24 | impl From for Uuid { 25 | fn from(arg: uuid::Uuid) -> Self { 26 | Self { inner: arg } 27 | } 28 | } 29 | 30 | impl From<&uuid::Uuid> for Uuid { 31 | fn from(arg: &uuid::Uuid) -> Self { 32 | Self { inner: arg.clone() } 33 | } 34 | } 35 | 36 | impl From for Uuid { 37 | fn from(arg: rbson::Binary) -> Self { 38 | let id = &String::from_utf8(arg.bytes).unwrap_or_default(); 39 | Uuid { 40 | inner: uuid::Uuid::from_str(&id).unwrap_or_default(), 41 | } 42 | } 43 | } 44 | 45 | impl serde::Serialize for Uuid { 46 | #[inline] 47 | fn serialize(&self, serializer: S) -> Result 48 | where 49 | S: Serializer, 50 | { 51 | use serde::ser::Error; 52 | if type_name::().eq("rbson::ser::error::Error") { 53 | return serializer.serialize_str(&format!("Uuid({})", self.inner)); 54 | } else { 55 | return self.inner.serialize(serializer); 56 | } 57 | } 58 | } 59 | 60 | impl<'de> serde::Deserialize<'de> for Uuid { 61 | #[inline] 62 | fn deserialize(deserializer: D) -> Result 63 | where 64 | D: Deserializer<'de>, 65 | { 66 | match Bson::deserialize(deserializer)? { 67 | Bson::String(s) => { 68 | if s.starts_with("Uuid(") && s.ends_with(")") { 69 | let inner_data = &s["Uuid(".len()..(s.len() - 1)]; 70 | return Ok(Self { 71 | inner: uuid::Uuid::parse_str(inner_data) 72 | .or_else(|e| Err(D::Error::custom(e.to_string())))?, 73 | }); 74 | } else { 75 | return Ok(Self { 76 | inner: uuid::Uuid::parse_str(&s) 77 | .or_else(|e| Err(D::Error::custom(e.to_string())))?, 78 | }); 79 | } 80 | } 81 | _ => Err(D::Error::custom("deserialize un supported bson type!")), 82 | } 83 | } 84 | } 85 | 86 | impl Deref for Uuid { 87 | type Target = uuid::Uuid; 88 | 89 | fn deref(&self) -> &Self::Target { 90 | &self.inner 91 | } 92 | } 93 | 94 | impl DerefMut for Uuid { 95 | fn deref_mut(&mut self) -> &mut Self::Target { 96 | &mut self.inner 97 | } 98 | } 99 | 100 | impl std::fmt::Display for Uuid { 101 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 102 | self.inner.to_string().fmt(f) 103 | } 104 | } 105 | 106 | impl std::fmt::Debug for Uuid { 107 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 108 | self.inner.to_string().fmt(f) 109 | } 110 | } 111 | 112 | impl Uuid { 113 | pub fn new() -> Uuid { 114 | let uuid = uuid::Uuid::new_v4(); 115 | Uuid { inner: uuid } 116 | } 117 | 118 | pub fn parse_str(arg: &str) -> crate::error::Result { 119 | Ok(Uuid { 120 | inner: uuid::Uuid::parse_str(arg)?, 121 | }) 122 | } 123 | } 124 | 125 | #[cfg(test)] 126 | mod test { 127 | use crate::types::Uuid; 128 | 129 | #[test] 130 | fn test_display() { 131 | println!("{}", Uuid::new()); 132 | } 133 | 134 | #[test] 135 | fn test_debug() { 136 | let uuid = Uuid::new(); 137 | println!("{:?}", uuid); 138 | } 139 | 140 | #[test] 141 | fn test_ser_de() { 142 | let b = Uuid::new(); 143 | let bsons = rbson::to_bson(&b).unwrap(); 144 | let b_de: Uuid = rbson::from_bson(bsons).unwrap(); 145 | assert_eq!(b, b_de); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /mybatis-core/src/types/decimal.rs: -------------------------------------------------------------------------------- 1 | use bigdecimal_::{BigDecimal, ParseBigDecimalError}; 2 | use rbson::spec::BinarySubtype; 3 | use rbson::Bson; 4 | use serde::de::{Error, Visitor}; 5 | use serde::{Deserializer, Serialize, Serializer}; 6 | use std::any::type_name; 7 | use std::fmt::Formatter; 8 | use std::ops::{Deref, DerefMut}; 9 | use std::str::FromStr; 10 | 11 | /// Mybatis Decimal 12 | #[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] 13 | pub struct Decimal { 14 | pub inner: BigDecimal, 15 | } 16 | 17 | impl From for Decimal { 18 | fn from(arg: BigDecimal) -> Self { 19 | Self { inner: arg } 20 | } 21 | } 22 | 23 | impl From<&BigDecimal> for Decimal { 24 | fn from(arg: &BigDecimal) -> Self { 25 | Self { inner: arg.clone() } 26 | } 27 | } 28 | 29 | impl serde::Serialize for Decimal { 30 | #[inline] 31 | fn serialize(&self, serializer: S) -> Result 32 | where 33 | S: Serializer, 34 | { 35 | use serde::ser::Error; 36 | if type_name::().eq("rbson::ser::error::Error") { 37 | return serializer.serialize_str(&format!("Decimal({})", self.inner)); 38 | } else { 39 | return self.inner.serialize(serializer); 40 | } 41 | } 42 | } 43 | 44 | /// Decimal allow deserialize by an String or Binary 45 | impl<'de> serde::Deserialize<'de> for Decimal { 46 | #[inline] 47 | fn deserialize(deserializer: D) -> Result 48 | where 49 | D: Deserializer<'de>, 50 | { 51 | let bson = rbson::Bson::deserialize(deserializer)?; 52 | return match bson { 53 | Bson::String(s) => { 54 | if s.starts_with("Decimal(") && s.ends_with(")") { 55 | let inner_data = &s["Decimal(".len()..(s.len() - 1)]; 56 | return Ok(Self { 57 | inner: BigDecimal::from_str(inner_data) 58 | .or_else(|e| Err(D::Error::custom(e.to_string())))?, 59 | }); 60 | } else { 61 | Ok(Self { 62 | inner: BigDecimal::from_str(s.as_str()).unwrap_or_default(), 63 | }) 64 | } 65 | } 66 | Bson::Int32(s) => Ok(Self { 67 | inner: BigDecimal::from(s), 68 | }), 69 | Bson::Int64(s) => Ok(Self { 70 | inner: BigDecimal::from(s), 71 | }), 72 | Bson::Decimal128(s) => Ok(Self { 73 | inner: BigDecimal::from_str(&s.to_string()).unwrap_or_default(), 74 | }), 75 | _ => Err(D::Error::custom("deserialize unsupported bson type!")), 76 | }; 77 | } 78 | } 79 | 80 | impl std::fmt::Display for Decimal { 81 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 82 | self.inner.fmt(f) 83 | } 84 | } 85 | 86 | impl std::fmt::Debug for Decimal { 87 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 88 | self.inner.fmt(f) 89 | } 90 | } 91 | 92 | impl Deref for Decimal { 93 | type Target = BigDecimal; 94 | 95 | fn deref(&self) -> &Self::Target { 96 | &self.inner 97 | } 98 | } 99 | 100 | impl DerefMut for Decimal { 101 | fn deref_mut(&mut self) -> &mut Self::Target { 102 | &mut self.inner 103 | } 104 | } 105 | 106 | impl Decimal { 107 | pub fn from(s: &str) -> Self { 108 | let b = BigDecimal::from_str(s).unwrap_or_default(); 109 | Self { inner: b } 110 | } 111 | 112 | /// create from str 113 | pub fn from_str(arg: &str) -> Result { 114 | let b = BigDecimal::from_str(arg)?; 115 | Ok(Self { inner: b }) 116 | } 117 | } 118 | 119 | #[cfg(test)] 120 | mod test { 121 | use crate::types::Decimal; 122 | use rbson::Bson; 123 | 124 | #[test] 125 | fn test_ser_de() { 126 | let b = Decimal::from("1"); 127 | let bsons = rbson::to_bson(&b).unwrap(); 128 | match &bsons { 129 | rbson::Bson::String(s) => { 130 | assert_eq!(s, "Decimal(1)"); 131 | } 132 | _ => { 133 | panic!("not str"); 134 | } 135 | } 136 | let b_de: Decimal = rbson::from_bson(bsons).unwrap(); 137 | assert_eq!(b, b_de); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /mybatis-macro/src/util.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use proc_macro2::{Ident, Span}; 4 | use quote::quote; 5 | use quote::ToTokens; 6 | use syn::{FnArg, ItemFn, ReturnType}; 7 | 8 | //find and check method return type 9 | pub(crate) fn find_return_type(target_fn: &ItemFn) -> proc_macro2::TokenStream { 10 | let mut return_ty = target_fn.sig.output.to_token_stream(); 11 | match &target_fn.sig.output { 12 | ReturnType::Type(_, b) => { 13 | return_ty = b.to_token_stream(); 14 | } 15 | _ => {} 16 | } 17 | let s = format!("{}", return_ty); 18 | if !s.contains(":: Result") && !s.starts_with("Result") { 19 | return_ty = quote! { 20 | mybatis::core :: Result <#return_ty> 21 | }; 22 | } 23 | return_ty 24 | } 25 | 26 | pub(crate) fn get_fn_args(target_fn: &ItemFn) -> Vec { 27 | let mut fn_arg_name_vec = vec![]; 28 | for arg in &target_fn.sig.inputs { 29 | match arg { 30 | FnArg::Typed(t) => { 31 | let arg_name = format!("{}", t.pat.to_token_stream()); 32 | fn_arg_name_vec.push(arg_name); 33 | //println!("arg_name {}", arg_name); 34 | } 35 | _ => {} 36 | } 37 | } 38 | fn_arg_name_vec 39 | } 40 | 41 | pub(crate) fn filter_fn_args( 42 | target_fn: &ItemFn, 43 | arg_name: &str, 44 | arg_type: &str, 45 | ) -> std::collections::HashMap { 46 | let mut map = HashMap::new(); 47 | for arg in &target_fn.sig.inputs { 48 | match arg { 49 | FnArg::Typed(t) => { 50 | let arg_name_value = format!("{}", t.pat.to_token_stream()); 51 | if arg_name.eq(&arg_name_value) { 52 | map.insert(arg_name.to_string(), arg_name_value.clone()); 53 | } 54 | let arg_type_name = t.ty.to_token_stream().to_string(); 55 | if arg_type.eq(&arg_type_name) { 56 | map.insert(arg_type.to_string(), arg_name_value.clone()); 57 | } 58 | } 59 | _ => {} 60 | } 61 | } 62 | map 63 | } 64 | 65 | pub(crate) fn get_page_req_ident(target_fn: &ItemFn, func_name: &str) -> Ident { 66 | let page_reqs = filter_fn_args(target_fn, "", "& PageRequest"); 67 | if page_reqs.len() > 1 { 68 | panic!( 69 | "[mybatis] {} only support on arg of '**:&PageRequest'!", 70 | func_name 71 | ); 72 | } 73 | if page_reqs.len() == 0 { 74 | panic!( 75 | "[mybatis] {} method arg must have arg Type '**:&PageRequest'!", 76 | func_name 77 | ); 78 | } 79 | let req = page_reqs 80 | .get("& PageRequest") 81 | .unwrap_or(&String::new()) 82 | .to_owned(); 83 | if req.eq("") { 84 | panic!( 85 | "[mybatis] {} method arg must have arg Type '**:&PageRequest'!", 86 | func_name 87 | ); 88 | } 89 | let req = Ident::new(&req, Span::call_site()); 90 | req 91 | } 92 | 93 | //find and check method return type 94 | pub(crate) fn find_fn_body(target_fn: &ItemFn) -> proc_macro2::TokenStream { 95 | //del todos 96 | let mut target_fn = target_fn.clone(); 97 | let mut new_stmts = vec![]; 98 | for x in &target_fn.block.stmts { 99 | let token = x 100 | .to_token_stream() 101 | .to_string() 102 | .replace("\n", "") 103 | .replace(" ", ""); 104 | if token.eq("todo!()") || token.eq("unimplemented!()") || token.eq("impled!()") { 105 | //nothing to do 106 | } else { 107 | new_stmts.push(x.to_owned()); 108 | } 109 | } 110 | target_fn.block.stmts = new_stmts; 111 | target_fn.block.to_token_stream() 112 | } 113 | 114 | pub(crate) fn is_fetch(return_source: &str) -> bool { 115 | let is_select = !return_source.contains("DBExecResult"); 116 | return is_select; 117 | } 118 | 119 | pub(crate) fn is_mybatis_ref(ty_stream: &str) -> bool { 120 | if ty_stream.contains("MybatisExecutor") 121 | || ty_stream.contains("Mybatis") 122 | || ty_stream.contains("MyBatisConnExecutor") 123 | || ty_stream.contains("MyBatisTxExecutor") 124 | || ty_stream.contains("MyBatisTxExecutorGuard") 125 | { 126 | return true; 127 | } 128 | false 129 | } 130 | -------------------------------------------------------------------------------- /mybatis-core/src/types/mod.rs: -------------------------------------------------------------------------------- 1 | pub const BINARY_SUBTYPE_JSON: u8 = 0x90; 2 | 3 | pub mod json; 4 | 5 | pub use json::*; 6 | use rbson::spec::BinarySubtype; 7 | use rbson::Bson; 8 | use std::fmt::Formatter; 9 | 10 | pub mod uuids; 11 | 12 | pub use uuids::*; 13 | 14 | pub mod bytes; 15 | 16 | pub use bytes::*; 17 | 18 | pub mod datetime_native; 19 | 20 | pub use datetime_native::*; 21 | 22 | pub mod datetime_utc; 23 | 24 | pub use datetime_utc::*; 25 | 26 | pub mod date_native; 27 | 28 | pub use date_native::*; 29 | 30 | pub mod date_utc; 31 | 32 | pub use date_utc::*; 33 | 34 | pub mod time_native; 35 | 36 | pub use time_native::*; 37 | 38 | pub mod time_utc; 39 | 40 | pub use time_utc::*; 41 | 42 | pub mod decimal; 43 | 44 | pub use decimal::*; 45 | 46 | pub mod timestamp; 47 | 48 | pub use timestamp::*; 49 | 50 | pub mod timestamp_z; 51 | 52 | pub use timestamp_z::*; 53 | 54 | pub mod bools; 55 | 56 | pub use bools::*; 57 | 58 | pub trait Format { 59 | fn do_format(&self) -> String; 60 | } 61 | 62 | impl Format for Bson { 63 | fn do_format(&self) -> String { 64 | match self { 65 | Bson::Double(d) => { 66 | format!("{}", d) 67 | } 68 | Bson::String(s) => { 69 | format!("\"{}\"", s) 70 | } 71 | Bson::Array(arr) => arr.do_format(), 72 | Bson::Document(d) => { 73 | let mut buf = String::new(); 74 | buf.push_str("{"); 75 | for (k, v) in d { 76 | buf.push_str("\""); 77 | buf.push_str(k); 78 | buf.push_str("\""); 79 | buf.push_str(":"); 80 | buf.push_str(&v.do_format()); 81 | buf.push_str(","); 82 | } 83 | buf.pop(); 84 | buf.push_str("}"); 85 | buf 86 | } 87 | Bson::Boolean(b) => { 88 | format!("{}", b) 89 | } 90 | Bson::Null => { 91 | format!("null") 92 | } 93 | Bson::Int32(i) => { 94 | format!("{}", i) 95 | } 96 | Bson::Int64(i) => { 97 | format!("{}", i) 98 | } 99 | Bson::UInt32(i) => { 100 | format!("{}", i) 101 | } 102 | Bson::UInt64(i) => { 103 | format!("{}", i) 104 | } 105 | Bson::Timestamp(s) => { 106 | format!("\"{}\"", Timestamp::from(s.clone())) 107 | } 108 | Bson::Binary(d) => { 109 | match d.subtype { 110 | BinarySubtype::Generic => { 111 | let bytes_len = d.bytes.len(); 112 | if bytes_len > 8192 { 113 | //> 1kb 114 | format!("bytes({})", d.bytes.len()) 115 | } else { 116 | self.to_string() 117 | } 118 | } 119 | BinarySubtype::Uuid => { 120 | format!("\"{}\"", crate::types::Uuid::from(d)) 121 | } 122 | BinarySubtype::UserDefined(type_id) => match type_id { 123 | crate::types::BINARY_SUBTYPE_JSON => { 124 | format!( 125 | "{}", 126 | String::from_utf8(d.bytes.to_owned()).unwrap_or_default() 127 | ) 128 | } 129 | _ => { 130 | format!("un supported!") 131 | } 132 | }, 133 | _ => { 134 | format!("un supported!") 135 | } 136 | } 137 | } 138 | Bson::DateTime(dt) => { 139 | format!("\"{}\"", DateTimeNative::from(dt.clone())) 140 | } 141 | Bson::Decimal128(d) => { 142 | format!("{}", d) 143 | } 144 | } 145 | } 146 | } 147 | 148 | impl Format for Vec { 149 | fn do_format(&self) -> String { 150 | let mut buf = String::new(); 151 | buf.push_str("["); 152 | for item in self { 153 | buf.push_str(&item.do_format()); 154 | buf.push_str(","); 155 | } 156 | buf.pop(); 157 | buf.push_str("]"); 158 | buf 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /mybatis-core/src/types/bools.rs: -------------------------------------------------------------------------------- 1 | use rbson::Bson; 2 | use serde::de::Error; 3 | use serde::{Deserializer, Serializer}; 4 | use std::ops::{Deref, DerefMut}; 5 | 6 | /// Mybatis Bool 7 | /// for example: 8 | /// let b = Bool::from(true); 9 | /// let b = Bool::from("true"); 10 | /// let b = Bool::from(1); 11 | /// 12 | /// 13 | /// 1,1.0,"1",true will be Bool{ inner: true } 14 | /// -1,0,"2",false...and..more will be Bool{ inner: false } 15 | #[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] 16 | pub struct Bool { 17 | pub inner: bool, 18 | } 19 | 20 | impl From for Bool { 21 | fn from(arg: bool) -> Self { 22 | Bool { inner: arg } 23 | } 24 | } 25 | 26 | impl From for Bool { 27 | fn from(arg: i32) -> Self { 28 | Bool { 29 | inner: { 30 | if arg == 1 { 31 | true 32 | } else { 33 | false 34 | } 35 | }, 36 | } 37 | } 38 | } 39 | 40 | impl From for Bool { 41 | fn from(arg: i64) -> Self { 42 | Bool { 43 | inner: { 44 | if arg == 1 { 45 | true 46 | } else { 47 | false 48 | } 49 | }, 50 | } 51 | } 52 | } 53 | 54 | impl From for Bool { 55 | fn from(arg: u32) -> Self { 56 | Bool { 57 | inner: { 58 | if arg == 1 { 59 | true 60 | } else { 61 | false 62 | } 63 | }, 64 | } 65 | } 66 | } 67 | 68 | impl From for Bool { 69 | fn from(arg: u64) -> Self { 70 | Bool { 71 | inner: { 72 | if arg == 1 { 73 | true 74 | } else { 75 | false 76 | } 77 | }, 78 | } 79 | } 80 | } 81 | 82 | impl From for Bool { 83 | fn from(arg: f64) -> Self { 84 | Bool { 85 | inner: { 86 | if arg == 1f64 { 87 | true 88 | } else { 89 | false 90 | } 91 | }, 92 | } 93 | } 94 | } 95 | 96 | impl From<&str> for Bool { 97 | fn from(arg: &str) -> Self { 98 | Bool { 99 | inner: { 100 | if arg == "true" || arg == "1" || arg == "1.0" { 101 | true 102 | } else { 103 | false 104 | } 105 | }, 106 | } 107 | } 108 | } 109 | 110 | impl serde::Serialize for Bool { 111 | #[inline] 112 | fn serialize(&self, serializer: S) -> Result 113 | where 114 | S: Serializer, 115 | { 116 | serializer.serialize_bool(self.inner) 117 | } 118 | } 119 | 120 | impl<'de> serde::Deserialize<'de> for Bool { 121 | #[inline] 122 | fn deserialize(deserializer: D) -> Result 123 | where 124 | D: Deserializer<'de>, 125 | { 126 | let bson = Bson::deserialize(deserializer)?; 127 | match bson { 128 | Bson::Boolean(data) => { 129 | return Ok(Bool { inner: data }); 130 | } 131 | Bson::Int32(data) => { 132 | return Ok(Bool::from(data)); 133 | } 134 | Bson::Int64(data) => { 135 | return Ok(Bool::from(data)); 136 | } 137 | Bson::Double(data) => { 138 | return Ok(Bool::from(data)); 139 | } 140 | Bson::UInt32(data) => { 141 | return Ok(Bool::from(data)); 142 | } 143 | Bson::UInt64(data) => { 144 | return Ok(Bool::from(data)); 145 | } 146 | Bson::String(data) => { 147 | return Ok(Bool::from(data.as_str())); 148 | } 149 | _ => Err(D::Error::custom("deserialize unsupported bson type!")), 150 | } 151 | } 152 | } 153 | 154 | impl Deref for Bool { 155 | type Target = bool; 156 | 157 | fn deref(&self) -> &Self::Target { 158 | &self.inner 159 | } 160 | } 161 | 162 | impl DerefMut for Bool { 163 | fn deref_mut(&mut self) -> &mut Self::Target { 164 | &mut self.inner 165 | } 166 | } 167 | 168 | #[cfg(test)] 169 | mod test { 170 | use crate::Bool; 171 | 172 | #[test] 173 | fn test_ser_de() { 174 | let b = Bool::from("1"); 175 | let bsons = rbson::to_bson(&b).unwrap(); 176 | let b_de: Bool = rbson::from_bson(bsons).unwrap(); 177 | assert_eq!(b, b_de); 178 | assert_eq!(b.inner, b_de.inner); 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /mybatis-macro/src/element_from.rs: -------------------------------------------------------------------------------- 1 | use crate::html_loader::Element; 2 | use crate::py_sql::NodeType; 3 | use std::collections::HashMap; 4 | 5 | pub fn as_elements(arg: Vec) -> Vec { 6 | let mut res = vec![]; 7 | for x in arg { 8 | res.push(Element::from(x)); 9 | } 10 | res 11 | } 12 | 13 | impl From for Element { 14 | fn from(arg: NodeType) -> Self { 15 | match arg { 16 | NodeType::NString(n) => { 17 | return Element { 18 | tag: "".to_string(), 19 | data: n.value, 20 | attrs: Default::default(), 21 | childs: vec![], 22 | }; 23 | } 24 | NodeType::NIf(n) => { 25 | let mut m = HashMap::new(); 26 | m.insert("test".to_string(), n.test); 27 | return Element { 28 | tag: "if".to_string(), 29 | data: "".to_string(), 30 | attrs: m, 31 | childs: as_elements(n.childs), 32 | }; 33 | } 34 | NodeType::NTrim(n) => { 35 | let mut m = HashMap::new(); 36 | m.insert("trim".to_string(), n.trim); 37 | return Element { 38 | tag: "trim".to_string(), 39 | data: "".to_string(), 40 | attrs: m, 41 | childs: as_elements(n.childs), 42 | }; 43 | } 44 | NodeType::NForEach(n) => { 45 | let mut m = HashMap::new(); 46 | m.insert("collection".to_string(), n.collection); 47 | m.insert("index".to_string(), n.index); 48 | m.insert("item".to_string(), n.item); 49 | return Element { 50 | tag: "foreach".to_string(), 51 | data: "".to_string(), 52 | attrs: m, 53 | childs: as_elements(n.childs), 54 | }; 55 | } 56 | NodeType::NChoose(n) => { 57 | let mut whens = as_elements(n.when_nodes); 58 | if let Some(v) = n.otherwise_node { 59 | whens.push(Element::from(*v)); 60 | } 61 | return Element { 62 | tag: "choose".to_string(), 63 | data: "".to_string(), 64 | attrs: Default::default(), 65 | childs: whens, 66 | }; 67 | } 68 | NodeType::NOtherwise(n) => { 69 | return Element { 70 | tag: "otherwise".to_string(), 71 | data: "".to_string(), 72 | attrs: Default::default(), 73 | childs: as_elements(n.childs), 74 | }; 75 | } 76 | NodeType::NWhen(n) => { 77 | let mut m = HashMap::new(); 78 | m.insert("test".to_string(), n.test); 79 | return Element { 80 | tag: "when".to_string(), 81 | data: "".to_string(), 82 | attrs: m, 83 | childs: as_elements(n.childs), 84 | }; 85 | } 86 | NodeType::NBind(n) => { 87 | let mut m = HashMap::new(); 88 | m.insert("name".to_string(), n.name); 89 | m.insert("value".to_string(), n.value); 90 | return Element { 91 | tag: "bind".to_string(), 92 | data: "".to_string(), 93 | attrs: m, 94 | childs: vec![], 95 | }; 96 | } 97 | NodeType::NSet(n) => { 98 | return Element { 99 | tag: "set".to_string(), 100 | data: "".to_string(), 101 | attrs: Default::default(), 102 | childs: as_elements(n.childs), 103 | }; 104 | } 105 | NodeType::NWhere(n) => { 106 | return Element { 107 | tag: "where".to_string(), 108 | data: "".to_string(), 109 | attrs: Default::default(), 110 | childs: as_elements(n.childs), 111 | }; 112 | } 113 | NodeType::NPrint(n) => { 114 | return Element { 115 | tag: "println".to_string(), 116 | data: "".to_string(), 117 | attrs: Default::default(), 118 | childs: vec![], 119 | }; 120 | } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /mybatis-macro/src/macros/mybatis_sql_impl.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Ident, Span}; 2 | use quote::quote; 3 | use quote::ToTokens; 4 | use syn; 5 | use syn::{AttributeArgs, FnArg, ItemFn}; 6 | 7 | use crate::proc_macro::TokenStream; 8 | use crate::util::{ 9 | find_fn_body, find_return_type, get_fn_args, get_page_req_ident, is_fetch, is_mybatis_ref, 10 | }; 11 | 12 | //impl mybatis_sql macro 13 | pub(crate) fn impl_macro_mybatis_sql(target_fn: &ItemFn, args: &AttributeArgs) -> TokenStream { 14 | let return_ty = find_return_type(target_fn); 15 | let func_name_ident = target_fn.sig.ident.to_token_stream(); 16 | 17 | let mut mybatis_ident = "".to_token_stream(); 18 | let mut mybatis_name = String::new(); 19 | for x in &target_fn.sig.inputs { 20 | match x { 21 | FnArg::Receiver(_) => {} 22 | FnArg::Typed(t) => { 23 | let ty_stream = t.ty.to_token_stream().to_string(); 24 | if is_mybatis_ref(&ty_stream) { 25 | mybatis_ident = t.pat.to_token_stream(); 26 | mybatis_name = mybatis_ident.to_string(); 27 | break; 28 | } 29 | } 30 | } 31 | } 32 | 33 | let sql_ident; 34 | if args.len() == 1 { 35 | if mybatis_name.is_empty() { 36 | panic!("[mybatis] you should add mybatis ref param rb:&Mybatis or rb: &mut MybatisExecutor<'_,'_> on '{}()'!", target_fn.sig.ident); 37 | } 38 | sql_ident = args 39 | .get(0) 40 | .expect("[mybatis] miss mybatis_sql macaro param!") 41 | .to_token_stream(); 42 | } else if args.len() == 2 { 43 | mybatis_ident = args 44 | .get(0) 45 | .expect("[mybatis] miss mybatis ident param!") 46 | .to_token_stream(); 47 | mybatis_name = format!("{}", mybatis_ident); 48 | sql_ident = args 49 | .get(1) 50 | .expect("[mybatis] miss mybatis_sql macro sql param!") 51 | .to_token_stream(); 52 | } else { 53 | panic!("[mybatis] Incorrect macro parameter length!"); 54 | } 55 | 56 | let func_args_stream = target_fn.sig.inputs.to_token_stream(); 57 | let fn_body = find_fn_body(target_fn); 58 | let is_async = target_fn.sig.asyncness.is_some(); 59 | if !is_async { 60 | panic!( 61 | "[mybatis] #[mybatis_plus] 'fn {}({})' must be async fn! ", 62 | func_name_ident, func_args_stream 63 | ); 64 | } 65 | let mut call_method = quote! {}; 66 | let is_fetch = is_fetch(&return_ty.to_string()); 67 | if is_fetch { 68 | call_method = quote! {fetch}; 69 | } else { 70 | call_method = quote! {exec}; 71 | } 72 | //check use page method 73 | let mut page_req_str = String::new(); 74 | let mut page_req = quote! {}; 75 | if return_ty.to_string().contains("Page <") 76 | && func_args_stream.to_string().contains("& PageRequest") 77 | { 78 | let req = get_page_req_ident(target_fn, &func_name_ident.to_string()); 79 | page_req_str = req.to_string(); 80 | page_req = quote! {,#req}; 81 | call_method = quote! {fetch_page}; 82 | } 83 | //append all args 84 | let sql_args_gen = 85 | filter_args_context_id(&mybatis_name, &get_fn_args(target_fn), &[page_req_str]); 86 | //gen rust code templete 87 | let gen_token_temple = quote! { 88 | pub async fn #func_name_ident(#func_args_stream) -> #return_ty{ 89 | let mut rb_args =vec![]; 90 | #sql_args_gen 91 | #fn_body 92 | use mybatis::executor::{Executor,ExecutorMut}; 93 | return #mybatis_ident.#call_method(&#sql_ident,rb_args #page_req).await; 94 | } 95 | }; 96 | return gen_token_temple.into(); 97 | } 98 | 99 | fn filter_args_context_id( 100 | mybatis_name: &str, 101 | fn_arg_name_vec: &Vec, 102 | skip_names: &[String], 103 | ) -> proc_macro2::TokenStream { 104 | let mut sql_args_gen = quote! {}; 105 | for item in fn_arg_name_vec { 106 | let item_ident = Ident::new(&item, Span::call_site()); 107 | let item_ident_name = item_ident.to_string(); 108 | if item.eq(&mybatis_name) { 109 | continue; 110 | } 111 | let mut do_continue = false; 112 | for x in skip_names { 113 | if x.eq(&item_ident_name) { 114 | do_continue = true; 115 | break; 116 | } 117 | } 118 | if do_continue { 119 | continue; 120 | } 121 | sql_args_gen = quote! { 122 | #sql_args_gen 123 | rb_args.push(rbson::to_bson(#item_ident).unwrap_or_default()); 124 | }; 125 | } 126 | sql_args_gen 127 | } 128 | -------------------------------------------------------------------------------- /mybatis-core/src/types/timestamp.rs: -------------------------------------------------------------------------------- 1 | use rbson::Bson; 2 | use serde::de::Error; 3 | use serde::{Deserializer, Serializer}; 4 | use sqlx_core::types::time; 5 | use std::alloc::Layout; 6 | use std::ops::{Deref, DerefMut}; 7 | 8 | /// Mybatis Timestamp 9 | /// Rust type Postgres type(s) 10 | /// time::PrimitiveDateTime TIMESTAMP 11 | /// time::OffsetDateTime TIMESTAMPTZ 12 | /// 13 | /// Rust type MySQL type(s) 14 | /// time::OffsetDateTime TIMESTAMP 15 | #[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] 16 | pub struct Timestamp { 17 | pub inner: time::OffsetDateTime, 18 | } 19 | 20 | impl From for Timestamp { 21 | fn from(arg: time::OffsetDateTime) -> Self { 22 | Self { inner: arg } 23 | } 24 | } 25 | 26 | impl From<&time::OffsetDateTime> for Timestamp { 27 | fn from(arg: &time::OffsetDateTime) -> Self { 28 | Self { inner: arg.clone() } 29 | } 30 | } 31 | 32 | impl serde::Serialize for Timestamp { 33 | #[inline] 34 | fn serialize(&self, serializer: S) -> Result 35 | where 36 | S: Serializer, 37 | { 38 | let bs = Timestamp::from_le_i64(self.inner.unix_timestamp()); 39 | return bs.serialize(serializer); 40 | } 41 | } 42 | 43 | impl<'de> serde::Deserialize<'de> for Timestamp { 44 | #[inline] 45 | fn deserialize(deserializer: D) -> Result 46 | where 47 | D: Deserializer<'de>, 48 | { 49 | match Bson::deserialize(deserializer)? { 50 | Bson::String(s) => { 51 | return Ok(Self { 52 | inner: time::OffsetDateTime::parse(&s, "%F %T %z") 53 | .or_else(|e| Err(D::Error::custom(e.to_string())))?, 54 | }); 55 | } 56 | Bson::Int64(data) => { 57 | return Ok(Timestamp::from_unix_timestamp(data)); 58 | } 59 | Bson::Timestamp(data) => { 60 | return Ok(Timestamp::from(data)); 61 | } 62 | _ => Err(D::Error::custom("deserialize un supported bson type!")), 63 | } 64 | } 65 | } 66 | 67 | impl Timestamp { 68 | pub fn as_timestamp(arg: &rbson::Timestamp) -> i64 { 69 | let upper = (arg.time.to_le() as u64) << 32; 70 | let lower = arg.increment.to_le() as u64; 71 | (upper | lower) as i64 72 | } 73 | 74 | pub fn from_le_i64(val: i64) -> rbson::Timestamp { 75 | let ts = val.to_le(); 76 | rbson::Timestamp { 77 | time: ((ts as u64) >> 32) as u32, 78 | increment: (ts & 0xFFFF_FFFF) as u32, 79 | } 80 | } 81 | } 82 | 83 | impl From for Timestamp { 84 | fn from(data: rbson::Timestamp) -> Self { 85 | let offset = time::OffsetDateTime::from_unix_timestamp(Timestamp::as_timestamp(&data)); 86 | Self { inner: offset } 87 | } 88 | } 89 | 90 | impl std::fmt::Display for Timestamp { 91 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 92 | self.inner.fmt(f) 93 | } 94 | } 95 | 96 | impl std::fmt::Debug for Timestamp { 97 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 98 | self.inner.fmt(f) 99 | } 100 | } 101 | 102 | impl Deref for Timestamp { 103 | type Target = time::OffsetDateTime; 104 | 105 | fn deref(&self) -> &Self::Target { 106 | &self.inner 107 | } 108 | } 109 | 110 | impl DerefMut for Timestamp { 111 | fn deref_mut(&mut self) -> &mut Self::Target { 112 | &mut self.inner 113 | } 114 | } 115 | 116 | impl Timestamp { 117 | // pub fn now() -> Self { 118 | // let offset_date_time = time::OffsetDateTime::try_now_local().unwrap(); 119 | 120 | // Self { 121 | // inner: time::OffsetDateTime::from_unix_timestamp(offset_date_time.unix_timestamp()) 122 | // } 123 | // } 124 | 125 | /// create from str 126 | pub fn from_str(arg: &str) -> Result { 127 | let inner = time::OffsetDateTime::parse(arg, "%F %T %z")?; 128 | Ok(Self { inner: inner }) 129 | } 130 | 131 | pub fn timestamp_millis(&self) -> i64 { 132 | self.inner.unix_timestamp() 133 | } 134 | 135 | pub fn from_unix_timestamp(arg: i64) -> Self { 136 | Self { 137 | inner: time::OffsetDateTime::from_unix_timestamp(arg), 138 | } 139 | } 140 | } 141 | 142 | #[cfg(test)] 143 | mod test { 144 | use super::*; 145 | 146 | // #[test] 147 | // fn test_ser_de() { 148 | // let b = Timestamp::now(); 149 | // let bsons = rbson::to_bson(&b).unwrap(); 150 | // let b_de: Timestamp = rbson::from_bson(bsons).unwrap(); 151 | // assert_eq!(b, b_de); 152 | // } 153 | } 154 | -------------------------------------------------------------------------------- /mybatis-sql/src/ops_eq.rs: -------------------------------------------------------------------------------- 1 | use crate::ops::PartialEq; 2 | use crate::ops::{AsProxy, Value}; 3 | use std::cmp::PartialEq as PE; 4 | 5 | impl PartialEq for &'_ Value { 6 | fn op_eq(&self, other: &Value) -> bool { 7 | self.eq(&other) 8 | } 9 | } 10 | 11 | impl PartialEq<&Value> for &'_ Value { 12 | fn op_eq(&self, other: &&Value) -> bool { 13 | self.eq(&*other) 14 | } 15 | } 16 | 17 | impl PartialEq<&&Value> for &'_ Value { 18 | fn op_eq(&self, other: &&&Value) -> bool { 19 | self.eq(&**other) 20 | } 21 | } 22 | 23 | impl PartialEq for &&'_ Value { 24 | fn op_eq(&self, other: &Value) -> bool { 25 | (*self).eq(&other) 26 | } 27 | } 28 | 29 | impl PartialEq<&Value> for Value { 30 | fn op_eq(&self, other: &&Value) -> bool { 31 | self.eq(&**other) 32 | } 33 | } 34 | impl PartialEq<&&Value> for Value { 35 | fn op_eq(&self, other: &&&Value) -> bool { 36 | self.eq(&***other) 37 | } 38 | } 39 | 40 | impl PartialEq for Value { 41 | fn op_eq(&self, other: &Value) -> bool { 42 | self.eq(other) 43 | } 44 | } 45 | 46 | /** 47 | eq base 48 | **/ 49 | fn eq_u64(value: &Value, other: u64) -> bool { 50 | value.u64().eq(&other) 51 | } 52 | 53 | fn eq_i64(value: &Value, other: i64) -> bool { 54 | value.i64().eq(&other) 55 | } 56 | 57 | fn eq_f64(value: &Value, other: f64) -> bool { 58 | value.f64().eq(&other) 59 | } 60 | 61 | fn eq_bool(value: &Value, other: bool) -> bool { 62 | value.as_bool().unwrap_or_default().eq(&other) 63 | } 64 | 65 | fn eq_str(value: &Value, other: &str) -> bool { 66 | value.as_str().unwrap_or_default().eq(other) 67 | } 68 | 69 | impl PartialEq for Value { 70 | fn op_eq(&self, other: &str) -> bool { 71 | eq_str(self, other) 72 | } 73 | } 74 | 75 | impl<'a> PartialEq<&'a str> for Value { 76 | fn op_eq(&self, other: &&str) -> bool { 77 | eq_str(self, *other) 78 | } 79 | } 80 | 81 | impl PartialEq for str { 82 | fn op_eq(&self, other: &Value) -> bool { 83 | eq_str(other, self) 84 | } 85 | } 86 | 87 | impl<'a> PartialEq for &'a str { 88 | fn op_eq(&self, other: &Value) -> bool { 89 | eq_str(other, *self) 90 | } 91 | } 92 | 93 | impl PartialEq<&str> for str { 94 | fn op_eq(&self, other: &&str) -> bool { 95 | self.eq(*other) 96 | } 97 | } 98 | 99 | impl PartialEq for Value { 100 | fn op_eq(&self, other: &String) -> bool { 101 | eq_str(self, other.as_str()) 102 | } 103 | } 104 | 105 | impl PartialEq for String { 106 | fn op_eq(&self, other: &Value) -> bool { 107 | eq_str(other, self.as_str()) 108 | } 109 | } 110 | 111 | macro_rules! impl_numeric_eq { 112 | ($($eq:ident [$($ty:ty)*])*) => { 113 | $($( 114 | impl PartialEq<$ty> for Value { 115 | fn op_eq(&self, other: &$ty) -> bool { 116 | $eq(self, *other as _) 117 | } 118 | } 119 | 120 | impl PartialEq for $ty { 121 | fn op_eq(&self, other: &Value) -> bool { 122 | $eq(other, *self as _) 123 | } 124 | } 125 | 126 | impl PartialEq<&Value> for $ty { 127 | fn op_eq(&self, other: &&Value) -> bool { 128 | $eq(*other, *self as _) 129 | } 130 | } 131 | 132 | impl PartialEq<&&Value> for $ty { 133 | fn op_eq(&self, other: &&&Value) -> bool { 134 | $eq(**other, *self as _) 135 | } 136 | } 137 | 138 | impl<'a> PartialEq<$ty> for &'a Value { 139 | fn op_eq(&self, other: &$ty) -> bool { 140 | $eq(*self, *other as _) 141 | } 142 | } 143 | )*)* 144 | } 145 | } 146 | 147 | impl_numeric_eq! { 148 | eq_i64[u8 u16 u32 u64] 149 | eq_i64[i8 i16 i32 i64 isize] 150 | eq_f64[f32 f64] 151 | eq_bool[bool] 152 | } 153 | 154 | macro_rules! eq_self { 155 | ([$($ty:ty)*]) => { 156 | $( 157 | impl PartialEq<$ty> for $ty{ 158 | fn op_eq(&self, rhs: &$ty) -> bool { 159 | self.eq(rhs) 160 | } 161 | } 162 | impl PartialEq<&$ty> for $ty{ 163 | fn op_eq(&self, rhs: &&$ty) -> bool { 164 | self.eq(*rhs) 165 | } 166 | } 167 | impl PartialEq<$ty> for &$ty{ 168 | fn op_eq(&self, rhs: &$ty) -> bool { 169 | self.eq(&rhs) 170 | } 171 | } 172 | impl PartialEq<&$ty> for &$ty{ 173 | fn op_eq(&self, rhs: &&$ty) -> bool { 174 | self.eq(rhs) 175 | } 176 | } 177 | )* 178 | }; 179 | } 180 | 181 | eq_self!([u8 u16 u32 u64]); 182 | eq_self!([i8 i16 i32 i64 isize]); 183 | eq_self!([f32 f64]); 184 | eq_self!([String & str]); 185 | -------------------------------------------------------------------------------- /example/src/wrapper_test.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use mybatis::wrapper::Wrapper; 4 | use mybatis_core::db::DriverType; 5 | use std::collections::{BTreeMap, HashMap}; 6 | 7 | #[test] 8 | fn test_item() { 9 | let w = Wrapper::new(&DriverType::Postgres).having("id"); 10 | assert_eq!(w.sql, "having id"); 11 | let mut m = BTreeMap::new(); 12 | m.insert("id", 2); 13 | m.insert("name", 1); 14 | let w = Wrapper::new(&DriverType::Postgres).all_eq(m); 15 | assert_eq!(w.sql, "(id = $1 and name = $2)"); 16 | let w = Wrapper::new(&DriverType::Postgres).eq("id", 1); 17 | assert_eq!(w.sql, "id = $1"); 18 | let w = Wrapper::new(&DriverType::Postgres).ne("id", 1); 19 | assert_eq!(w.sql, "id <> $1"); 20 | let w = Wrapper::new(&DriverType::Postgres).order_by(true, &["id"]); 21 | assert_eq!(w.sql, " order by id asc "); 22 | let w = Wrapper::new(&DriverType::Postgres).group_by(&["id"]); 23 | assert_eq!(w.sql, " group by id "); 24 | let w = Wrapper::new(&DriverType::Postgres).gt("id", 1); 25 | assert_eq!(w.sql, "id > $1"); 26 | let w = Wrapper::new(&DriverType::Postgres).ge("id", 1); 27 | assert_eq!(w.sql, "id >= $1"); 28 | let w = Wrapper::new(&DriverType::Postgres).lt("id", 1); 29 | assert_eq!(w.sql, "id < $1"); 30 | let w = Wrapper::new(&DriverType::Postgres).le("id", 1); 31 | assert_eq!(w.sql, "id <= $1"); 32 | let w = Wrapper::new(&DriverType::Postgres).between("id", 1, 2); 33 | assert_eq!(w.sql, "id between $1 and $2"); 34 | let w = Wrapper::new(&DriverType::Postgres).not_between("id", 1, 2); 35 | assert_eq!(w.sql, "id not between $1 and $2"); 36 | let w = Wrapper::new(&DriverType::Postgres).like("id", 1); 37 | assert_eq!(w.sql, "id like $1"); 38 | let w = Wrapper::new(&DriverType::Postgres).like_left("id", 1); 39 | assert_eq!(w.sql, "id like $1"); 40 | let w = Wrapper::new(&DriverType::Postgres).like_right("id", 1); 41 | assert_eq!(w.sql, "id like $1"); 42 | let w = Wrapper::new(&DriverType::Postgres).not_like("id", 1); 43 | assert_eq!(w.sql, "id not like $1"); 44 | let w = Wrapper::new(&DriverType::Postgres).is_null("id"); 45 | assert_eq!(w.sql, "id is NULL"); 46 | let w = Wrapper::new(&DriverType::Postgres).is_not_null("id"); 47 | assert_eq!(w.sql, "id is not NULL"); 48 | let w = Wrapper::new(&DriverType::Postgres).in_array("id", &[1]); 49 | assert_eq!(w.sql, "id in ( $1 )"); 50 | let w = Wrapper::new(&DriverType::Postgres).not_in("id", &[1]); 51 | assert_eq!(w.sql, "id not in ( $1 )"); 52 | let w = Wrapper::new(&DriverType::Postgres).insert_into("table", "c", "v"); 53 | assert_eq!(w.sql, "insert into table (c) values (v)"); 54 | let w = Wrapper::new(&DriverType::Postgres).limit(1); 55 | assert_eq!(w.sql, " limit 1 "); 56 | let w = Wrapper::new(&DriverType::Postgres).order_bys(&[("id", true), ("name", false)]); 57 | assert_eq!(w.sql, " order by id asc,name desc "); 58 | } 59 | 60 | #[test] 61 | fn test_all() { 62 | let mut m = BTreeMap::new(); 63 | m.insert("id", 2); 64 | m.insert("name", 1); 65 | let mut w = Wrapper::new(&DriverType::Postgres); 66 | w = w.having("id"); 67 | w = w.all_eq(m); 68 | w = w.eq("id", 1); 69 | w = w.ne("id", 1); 70 | w = w.gt("id", 1); 71 | w = w.ge("id", 1); 72 | w = w.lt("id", 1); 73 | w = w.le("id", 1); 74 | w = w.between("id", 1, 2); 75 | w = w.not_between("id", 1, 2); 76 | w = w.like("id", 1); 77 | w = w.like_left("id", 1); 78 | w = w.like_right("id", 1); 79 | w = w.not_like("id", 1); 80 | w = w.is_null("id"); 81 | w = w.is_not_null("id"); 82 | w = w.in_array("id", &[1]); 83 | w = w.not_in("id", &[1]); 84 | w = w.order_by(true, &["id"]); 85 | w = w.group_by(&["id"]); 86 | w = w.limit(1); 87 | w = w.order_bys(&[("id", true), ("name", false)]); 88 | assert_eq!(w.sql, "having id and (id = $1 and name = $2) and id = $3 and id <> $4 and id > $5 and id >= $6 and id < $7 and id <= $8 and id between $9 and $10 and id not between $11 and $12 and id like $13 and id like $14 and id like $15 and id not like $16 and id is NULL and id is not NULL and id in ( $17 ) and id not in ( $18 ) order by id asc group by id limit 1 order by id asc,name desc "); 89 | } 90 | 91 | #[test] 92 | fn test_hash_map() { 93 | let mut n = HashMap::new(); 94 | n.insert("id", 3); 95 | n.insert("name", 8); 96 | 97 | let mut w = Wrapper::new(&DriverType::Postgres); 98 | w = w.all_eq(n); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /mybatis-core/src/types/datetime_utc.rs: -------------------------------------------------------------------------------- 1 | use chrono::{DateTime, Utc}; 2 | use rbson::spec::BinarySubtype; 3 | use rbson::Bson; 4 | use serde::de::Error; 5 | use serde::{Deserializer, Serializer}; 6 | use std::any::type_name; 7 | use std::ops::{Deref, DerefMut}; 8 | use std::str::FromStr; 9 | 10 | /// Mybatis DateTime Utc 11 | #[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] 12 | pub struct DateTimeUtc { 13 | pub inner: chrono::DateTime, 14 | } 15 | 16 | impl From> for DateTimeUtc { 17 | fn from(arg: chrono::DateTime) -> Self { 18 | DateTimeUtc { inner: arg } 19 | } 20 | } 21 | 22 | impl From<&chrono::DateTime> for DateTimeUtc { 23 | fn from(arg: &chrono::DateTime) -> Self { 24 | DateTimeUtc { inner: arg.clone() } 25 | } 26 | } 27 | 28 | impl serde::Serialize for DateTimeUtc { 29 | #[inline] 30 | fn serialize(&self, serializer: S) -> Result 31 | where 32 | S: Serializer, 33 | { 34 | use serde::ser::Error; 35 | if type_name::().eq("rbson::ser::error::Error") { 36 | return serializer.serialize_str(&format!("DateTimeUtc({})", self.inner)); 37 | } else { 38 | return self.inner.serialize(serializer); 39 | } 40 | } 41 | } 42 | 43 | impl<'de> serde::Deserialize<'de> for DateTimeUtc { 44 | #[inline] 45 | fn deserialize(deserializer: D) -> Result 46 | where 47 | D: Deserializer<'de>, 48 | { 49 | match Bson::deserialize(deserializer)? { 50 | Bson::DateTime(date) => { 51 | return Ok(Self { 52 | inner: date.to_chrono(), 53 | }); 54 | } 55 | Bson::String(s) => { 56 | let mut b = s.into_bytes(); 57 | if b.len() >= 10 && b[10] == ' ' as u8 { 58 | b[10] = 'T' as u8; 59 | } 60 | let s = unsafe { String::from_utf8_unchecked(b) }; 61 | if s.starts_with("DateTimeUtc(") && s.ends_with(")") { 62 | let inner_data = &s["DateTimeUtc(".len()..(s.len() - 1)]; 63 | return Ok(Self { 64 | inner: chrono::DateTime::::from_str(inner_data) 65 | .or_else(|e| Err(D::Error::custom(e.to_string())))?, 66 | }); 67 | } else { 68 | return Ok(Self { 69 | inner: chrono::DateTime::::from_str(&s) 70 | .or_else(|e| Err(D::Error::custom(e.to_string())))?, 71 | }); 72 | } 73 | } 74 | //timestamp(ms) 75 | Bson::Int64(timestamp) => { 76 | let native = chrono::NaiveDateTime::from_timestamp( 77 | timestamp / 1000, 78 | (timestamp % 1000 * 1000000) as u32, 79 | ); 80 | return Ok(DateTimeUtc { 81 | inner: DateTime::::from_utc(native, Utc), 82 | }); 83 | } 84 | _ => Err(D::Error::custom("deserialize un supported bson type!")), 85 | } 86 | } 87 | } 88 | 89 | impl std::fmt::Display for DateTimeUtc { 90 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 91 | self.inner.fmt(f) 92 | } 93 | } 94 | 95 | impl std::fmt::Debug for DateTimeUtc { 96 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 97 | self.inner.fmt(f) 98 | } 99 | } 100 | 101 | impl Deref for DateTimeUtc { 102 | type Target = chrono::DateTime; 103 | 104 | fn deref(&self) -> &Self::Target { 105 | &self.inner 106 | } 107 | } 108 | 109 | impl DerefMut for DateTimeUtc { 110 | fn deref_mut(&mut self) -> &mut Self::Target { 111 | &mut self.inner 112 | } 113 | } 114 | 115 | impl DateTimeUtc { 116 | /// Returns a [`DateTime`] which corresponds to the current date and time. 117 | pub fn now() -> DateTimeUtc { 118 | let utc = Utc::now(); 119 | let dt = rbson::DateTime::from_millis(utc.timestamp_millis()); 120 | Self { 121 | inner: dt.to_chrono(), 122 | } 123 | } 124 | 125 | /// create from str 126 | pub fn from_str(arg: &str) -> Result { 127 | let inner = chrono::DateTime::::from_str(arg)?; 128 | Ok(Self { inner: inner }) 129 | } 130 | } 131 | 132 | #[cfg(test)] 133 | mod test { 134 | use crate::types::DateTimeUtc; 135 | 136 | #[test] 137 | fn test_ser_de() { 138 | let b = DateTimeUtc::now(); 139 | let bsons = rbson::to_bson(&b).unwrap(); 140 | let b_de: DateTimeUtc = rbson::from_bson(bsons).unwrap(); 141 | assert_eq!(b, b_de); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /mybatis-core/src/decode.rs: -------------------------------------------------------------------------------- 1 | //! Types and traits for decoding values from the database. 2 | use log::info; 3 | use rbson::Bson; 4 | use serde::de::DeserializeOwned; 5 | 6 | use crate::types::Format; 7 | use crate::Error; 8 | use crate::Error::E; 9 | 10 | /// decode json vec to an object 11 | /// support decode types: 12 | /// Bson,BigDecimal, i8..i64,u8..u64,rbson::Int64(),bool,String 13 | /// or object used bson macro object 14 | pub fn decode(datas: rbson::Array) -> Result 15 | where 16 | T: DeserializeOwned, 17 | { 18 | let type_name = std::any::type_name::(); 19 | //debug_mode feature print json_decode json data 20 | let bs = Bson::Array(datas); 21 | #[cfg(feature = "debug_mode")] 22 | { 23 | println!("[mybatis] [debug_mode] [bson] {} => {}", type_name, bs); 24 | #[cfg(feature = "format_bson")] 25 | println!( 26 | "[mybatis] [debug_mode] [format] {} => {}", 27 | type_name, 28 | bs.as_array().unwrap().do_format() 29 | ); 30 | } 31 | let mut datas = vec![]; 32 | match bs { 33 | Bson::Array(arr) => { 34 | datas = arr; 35 | } 36 | _ => {} 37 | } 38 | // Hit a non-array object 39 | match type_name { 40 | //decode single type option 41 | "core::option::Option" | "core::option::Option" | "core::option::Option" | "core::option::Option" | 42 | "core::option::Option" | "core::option::Option" | "core::option::Option" | "core::option::Option" | 43 | "core::option::Option" | "core::option::Option" | 44 | "core::option::Option" | 45 | "core::option::Option" | "core::option::Option" | "core::option::Option" | 46 | "core::option::Option" | 47 | "core::option::Option" | 48 | "core::option::Option" | 49 | //decode single type(from map type get an value) 50 | "i8" | "i16" | "i32" | "i64" | 51 | "u8" | "u16" | "u32" | "u64" | 52 | "f32" | "f64" | 53 | "serde_json::number::Number" | 54 | "rbson::Bson::Int64" | "rbson::Bson::Int32" | "rbson::Bson::Double" | 55 | "bigdecimal::BigDecimal" | 56 | "bool" | 57 | "alloc::string::String" => { 58 | return Ok(try_decode_doc(type_name, &mut datas)?); 59 | } 60 | _ => {} 61 | } 62 | // try speculate type 63 | let is_array: Result = rbson::from_bson(rbson::Bson::Array(vec![])); 64 | if is_array.is_ok() { 65 | //decode array 66 | Ok(rbson::from_bson(Bson::Array(datas))?) 67 | } else { 68 | Ok(try_decode_doc(type_name, &mut datas)?) 69 | } 70 | } 71 | 72 | //decode doc or one type 73 | pub fn try_decode_doc(type_name: &str, datas: &mut Vec) -> Result 74 | where 75 | T: DeserializeOwned, 76 | { 77 | //decode struct 78 | if datas.len() > 1 { 79 | return Result::Err(Error::from(format!( 80 | "[mybatis] rows.rows_affected > 1,but decode one type ({})!", 81 | type_name 82 | ))); 83 | } 84 | //single try decode 85 | if datas.is_empty() { 86 | return Ok(rbson::from_bson::(Bson::Null)?); 87 | } 88 | let mut v = Bson::Null; 89 | let m = datas.remove(0); 90 | let mut doc_len = 0; 91 | match &m { 92 | Bson::Document(doc) => { 93 | doc_len = doc.len(); 94 | if doc_len == 1 { 95 | for (_k, _v) in doc { 96 | v = _v.clone(); 97 | break; 98 | } 99 | } 100 | } 101 | _ => {} 102 | } 103 | let r = rbson::from_bson::(m); 104 | if r.is_err() { 105 | if doc_len > 1 { 106 | return Ok(r?); 107 | } 108 | //try one 109 | return Ok(rbson::from_bson::(v)?); 110 | } else { 111 | return Ok(r.unwrap()); 112 | } 113 | } 114 | 115 | #[cfg(test)] 116 | mod test { 117 | use crate::decode::decode; 118 | use crate::types::Json; 119 | use rbson::bson; 120 | use rbson::Bson; 121 | use std::collections::{BTreeMap, HashMap, HashSet, VecDeque}; 122 | 123 | #[test] 124 | fn test_decode_hashmap() { 125 | let m: HashMap = decode(vec![bson!( 126 | { 127 | "a":"1", 128 | "b":2 129 | } 130 | )]) 131 | .unwrap(); 132 | println!("{:#?}", m); 133 | } 134 | 135 | #[test] 136 | fn test_decode_btree_map() { 137 | let m: BTreeMap = decode(vec![bson!( 138 | { 139 | "a":"1", 140 | "b":2 141 | } 142 | )]) 143 | .unwrap(); 144 | println!("{:#?}", m); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /example/src/pets.rs: -------------------------------------------------------------------------------- 1 | use mybatis::mybatis_sql::string_util::to_snake_name; 2 | use mybatis::plus::MybatisPlus; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | #[derive(Debug, Serialize, Deserialize)] 6 | pub struct Pets { 7 | pub id: Option, 8 | pub name: Option, 9 | pub birthday: Option, 10 | pub delete_flag: Option, 11 | } 12 | 13 | impl MybatisPlus for Pets { 14 | fn table_name() -> String { 15 | let type_name = std::any::type_name::(); 16 | let mut name = type_name.to_string(); 17 | let names: Vec<&str> = name.split("::").collect(); 18 | name = names.get(names.len() - 1).unwrap_or(&"").to_string(); 19 | 20 | to_snake_name(&name) 21 | } 22 | 23 | fn table_columns() -> String { 24 | String::from("id,name,birthday,delete_flag") 25 | } 26 | } 27 | 28 | #[cfg(test)] 29 | mod tests { 30 | use mybatis::mybatis::Mybatis; 31 | use mybatis::plus::{Mapping, Skip}; 32 | use mybatis::snowflake::SNOWFLAKE; 33 | 34 | use super::*; 35 | 36 | #[tokio::test] 37 | async fn save_pets() { 38 | let mybatis = Mybatis::new(); 39 | 40 | mybatis 41 | .link("mysql://root:passw0rd@localhost:3306/test") 42 | .await 43 | .unwrap(); 44 | 45 | let id = SNOWFLAKE.generate(); 46 | let cat = Pets { 47 | id: Some(id.to_string()), 48 | name: Some("Cindy".to_string()), 49 | birthday: Some(mybatis::DateTimeNative::now()), 50 | delete_flag: Some(0), 51 | }; 52 | 53 | mybatis.save(&cat, &[]).await; 54 | } 55 | 56 | #[tokio::test] 57 | async fn save_batch_pets() { 58 | let mybatis = Mybatis::new(); 59 | 60 | mybatis 61 | .link("mysql://root:passw0rd@localhost:3306/test") 62 | .await 63 | .unwrap(); 64 | 65 | let cat = Pets { 66 | id: Some(SNOWFLAKE.generate().to_string()), 67 | name: Some("Tom".to_string()), 68 | birthday: Some(mybatis::DateTimeNative::now()), 69 | delete_flag: Some(0), 70 | }; 71 | 72 | let dog = Pets { 73 | id: Some(SNOWFLAKE.generate().to_string()), 74 | name: Some("Jerry".to_string()), 75 | birthday: Some(mybatis::DateTimeNative::now()), 76 | delete_flag: Some(0), 77 | }; 78 | 79 | mybatis.save_batch(&vec![cat, dog], &[]).await; 80 | } 81 | 82 | #[tokio::test] 83 | async fn query_all_pets() { 84 | let mybatis = Mybatis::new(); 85 | 86 | mybatis 87 | .link("mysql://root:passw0rd@localhost:3306/test") 88 | .await 89 | .unwrap(); 90 | 91 | let result: Vec = mybatis.fetch_list().await.unwrap(); 92 | println!("result: {:?}", result); 93 | } 94 | 95 | #[tokio::test] 96 | async fn query_pet_by_name() { 97 | let mybatis = Mybatis::new(); 98 | 99 | mybatis 100 | .link("mysql://root:passw0rd@localhost:3306/test") 101 | .await 102 | .unwrap(); 103 | 104 | let result: Option = mybatis.fetch_by_column("name", &"Cindy").await.unwrap(); 105 | println!("result: {:?}", result); 106 | } 107 | 108 | #[tokio::test] 109 | async fn update_pet_by_name() { 110 | let mybatis = Mybatis::new(); 111 | 112 | mybatis 113 | .link("mysql://root:passw0rd@localhost:3306/test") 114 | .await 115 | .unwrap(); 116 | 117 | let update_wrapper = mybatis.new_wrapper().eq("name", "Tom"); 118 | 119 | let james = Pets { 120 | id: None, 121 | name: Some("James".to_string()), 122 | birthday: None, 123 | delete_flag: None, 124 | }; 125 | 126 | let mut skip_columns = Vec::new(); 127 | skip_columns.push(Skip::Column("id")); 128 | skip_columns.push(Skip::Column("birthday")); 129 | skip_columns.push(Skip::Column("delete_flag")); 130 | 131 | mybatis 132 | .update_by_wrapper(&james, update_wrapper, &skip_columns) 133 | .await; 134 | } 135 | 136 | #[tokio::test] 137 | async fn delete_pet_by_name() { 138 | let mybatis = Mybatis::new(); 139 | 140 | mybatis 141 | .link("mysql://root:passw0rd@localhost:3306/test") 142 | .await 143 | .unwrap(); 144 | mybatis.remove_by_column::("name", "James").await; 145 | } 146 | 147 | #[tokio::test] 148 | async fn delete_betch_pet_by_name() { 149 | let mybatis = Mybatis::new(); 150 | 151 | mybatis 152 | .link("mysql://root:passw0rd@localhost:3306/test") 153 | .await 154 | .unwrap(); 155 | 156 | mybatis 157 | .remove_batch_by_column::("name", &["Cindy", "Jerry"]) 158 | .await; 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /mybatis-core/src/types/json.rs: -------------------------------------------------------------------------------- 1 | use crate::types::BINARY_SUBTYPE_JSON; 2 | use rbson::spec::BinarySubtype; 3 | use rbson::{Binary, Bson, Document}; 4 | use serde::de::{DeserializeOwned, Error, Expected}; 5 | use serde::{Deserializer, Serialize, Serializer}; 6 | use serde_json::ser::Formatter; 7 | use serde_json::Value; 8 | use std::any::type_name; 9 | use std::borrow::BorrowMut; 10 | use std::ops::{Deref, DerefMut}; 11 | use std::slice::IterMut; 12 | 13 | /// Json 14 | #[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] 15 | pub struct Json 16 | where 17 | T: Serialize, 18 | { 19 | pub inner: T, 20 | } 21 | 22 | impl From for Json 23 | where 24 | T: Serialize, 25 | { 26 | fn from(arg: T) -> Self { 27 | Self { inner: arg } 28 | } 29 | } 30 | 31 | impl From<&T> for Json 32 | where 33 | T: Serialize + Clone, 34 | { 35 | fn from(arg: &T) -> Self { 36 | Self { inner: arg.clone() } 37 | } 38 | } 39 | 40 | impl serde::Serialize for Json { 41 | #[inline] 42 | fn serialize(&self, serializer: S) -> Result 43 | where 44 | S: Serializer, 45 | { 46 | use serde::ser::Error; 47 | if type_name::().eq("rbson::ser::error::Error") { 48 | return Binary { 49 | subtype: BinarySubtype::UserDefined(BINARY_SUBTYPE_JSON), 50 | bytes: serde_json::to_vec(&self.inner).unwrap_or_default(), 51 | } 52 | .serialize(serializer); 53 | } else { 54 | return self.inner.serialize(serializer); 55 | } 56 | } 57 | } 58 | 59 | impl<'de, T> serde::Deserialize<'de> for Json 60 | where 61 | T: Serialize + DeserializeOwned, 62 | { 63 | #[inline] 64 | fn deserialize(deserializer: D) -> Result 65 | where 66 | D: Deserializer<'de>, 67 | { 68 | let b = Bson::deserialize(deserializer)?; 69 | match b { 70 | Bson::String(s) => { 71 | return Ok(Self { 72 | inner: serde_json::from_str(&s) 73 | .or_else(|e| Err(D::Error::custom(e.to_string())))?, 74 | }); 75 | } 76 | Bson::Binary(data) => { 77 | let v = serde_json::from_slice::(&data.bytes) 78 | .or_else(|e| Err(D::Error::custom(e.to_string())))?; 79 | Ok(Json { inner: v }) 80 | } 81 | Bson::Decimal128(v) => { 82 | let v = serde_json::from_value::(serde_json::Value::String(v.to_string())) 83 | .or_else(|e| Err(D::Error::custom(e.to_string())))?; 84 | Ok(Json { inner: v }) 85 | } 86 | _ => { 87 | let v = serde_json::from_value::(b.into_canonical_extjson()) 88 | .or_else(|e| Err(D::Error::custom(e.to_string())))?; 89 | Ok(Json { inner: v }) 90 | } 91 | } 92 | } 93 | } 94 | 95 | impl std::fmt::Display for Json 96 | where 97 | T: std::fmt::Display, 98 | { 99 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 100 | self.inner.fmt(f) 101 | } 102 | } 103 | 104 | impl std::fmt::Debug for Json 105 | where 106 | T: std::fmt::Debug, 107 | { 108 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 109 | self.inner.fmt(f) 110 | } 111 | } 112 | 113 | impl Json { 114 | pub fn to_string(&self) -> String 115 | where 116 | T: std::fmt::Display, 117 | { 118 | self.inner.to_string() 119 | } 120 | } 121 | 122 | impl Deref for Json { 123 | type Target = T; 124 | 125 | fn deref(&self) -> &Self::Target { 126 | &self.inner 127 | } 128 | } 129 | 130 | impl DerefMut for Json { 131 | fn deref_mut(&mut self) -> &mut Self::Target { 132 | &mut self.inner 133 | } 134 | } 135 | 136 | impl Json 137 | where 138 | T: Serialize, 139 | { 140 | pub fn from_value(arg: Value) -> Self 141 | where 142 | T: Serialize + DeserializeOwned + Default, 143 | { 144 | Json { 145 | inner: serde_json::from_value(arg).unwrap_or_default(), 146 | } 147 | } 148 | } 149 | 150 | impl Json 151 | where 152 | T: Serialize + DeserializeOwned, 153 | { 154 | /// create from str 155 | pub fn from_str(arg: &str) -> Result { 156 | let inner = serde_json::from_str(arg)?; 157 | Ok(Self { inner: inner }) 158 | } 159 | } 160 | 161 | #[cfg(test)] 162 | mod test { 163 | use crate::types::Json; 164 | 165 | #[test] 166 | fn test_ser_de() { 167 | let b = Json { inner: 1 }; 168 | let bsons = rbson::to_bson(&b).unwrap(); 169 | let b_de: Json = rbson::from_bson(bsons).unwrap(); 170 | assert_eq!(b, b_de); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /mybatis-macro/src/macros/py_sql_impl.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Ident, Span}; 2 | use quote::quote; 3 | use quote::ToTokens; 4 | use syn::{AttributeArgs, FnArg, ItemFn}; 5 | 6 | use crate::proc_macro::TokenStream; 7 | use crate::util::{ 8 | find_fn_body, find_return_type, get_fn_args, get_page_req_ident, is_fetch, is_mybatis_ref, 9 | }; 10 | 11 | ///py_sql macro 12 | ///support args for &Mybatis,page:&PageRequest 13 | ///support return for Page<*> 14 | pub(crate) fn impl_macro_py_sql(target_fn: &ItemFn, args: &AttributeArgs) -> TokenStream { 15 | let return_ty = find_return_type(target_fn); 16 | let mut mybatis_ident = "".to_token_stream(); 17 | let mut mybatis_name = String::new(); 18 | for x in &target_fn.sig.inputs { 19 | match x { 20 | FnArg::Receiver(_) => {} 21 | FnArg::Typed(t) => { 22 | let ty_stream = t.ty.to_token_stream().to_string(); 23 | if is_mybatis_ref(&ty_stream) { 24 | mybatis_ident = t.pat.to_token_stream(); 25 | mybatis_name = mybatis_ident.to_string(); 26 | break; 27 | } 28 | } 29 | } 30 | } 31 | 32 | let sql_ident; 33 | if args.len() == 1 { 34 | if mybatis_name.is_empty() { 35 | panic!("[mybatis] you should add mybatis ref param mybatis:&Mybatis or mybatis: &mut MybatisExecutor<'_,'_> on '{}()'!", target_fn.sig.ident); 36 | } 37 | sql_ident = args 38 | .get(0) 39 | .expect("[mybatis] miss pysql sql param!") 40 | .to_token_stream(); 41 | } else if args.len() == 2 { 42 | mybatis_ident = args 43 | .get(0) 44 | .expect("[mybatis] miss mybatis ident param!") 45 | .to_token_stream(); 46 | mybatis_name = format!("{}", mybatis_ident); 47 | sql_ident = args 48 | .get(1) 49 | .expect("[mybatis] miss pysql sql param!") 50 | .to_token_stream(); 51 | } else { 52 | panic!("[mybatis] Incorrect macro parameter length!"); 53 | } 54 | 55 | let func_args_stream = target_fn.sig.inputs.to_token_stream(); 56 | let fn_body = find_fn_body(target_fn); 57 | let is_async = target_fn.sig.asyncness.is_some(); 58 | let func_name_ident = target_fn.sig.ident.to_token_stream(); 59 | if !is_async { 60 | panic!( 61 | "[mybatis] #[mybatis_plus] 'fn {}({})' must be async fn! ", 62 | func_name_ident, func_args_stream 63 | ); 64 | } 65 | //append all args 66 | let sql_args_gen = filter_args_context_id(&mybatis_name, &get_fn_args(target_fn)); 67 | let is_fetch = is_fetch(&return_ty.to_string()); 68 | let mut call_method = quote! {}; 69 | if is_fetch { 70 | call_method = quote! { 71 | use mybatis::executor::{Executor,ExecutorMut}; 72 | #mybatis_ident.fetch(&sql,mybatis_args).await 73 | }; 74 | } else { 75 | call_method = quote! { 76 | use mybatis::executor::{Executor,ExecutorMut}; 77 | #mybatis_ident.exec(&sql,mybatis_args).await 78 | }; 79 | } 80 | if return_ty.to_string().contains("Page <") 81 | && func_args_stream.to_string().contains("& PageRequest") 82 | { 83 | let page_ident = get_page_req_ident(target_fn, &func_name_ident.to_string()); 84 | call_method = quote! { 85 | use mybatis::plus::{Mapping, MappingMut}; 86 | #mybatis_ident.fetch_page(&sql,mybatis_args,#page_ident).await 87 | }; 88 | println!("gen return"); 89 | } 90 | 91 | //gen rust code templete 92 | return quote! { 93 | pub async fn #func_name_ident(#func_args_stream) -> #return_ty { 94 | let mut sql = #sql_ident.to_string(); 95 | let mut mybatis_arg_map = rbson::Document::new(); 96 | #sql_args_gen 97 | #fn_body 98 | use mybatis::executor::{MybatisRef}; 99 | let driver_type = #mybatis_ident.get_mybatis().driver_type()?; 100 | use mybatis::{mybatis_sql,AsSqlTag}; 101 | let sql_tag = driver_type.sql_tag(); 102 | #[py(#sql_ident)] 103 | pub fn #func_name_ident(arg: &rbson::Bson, _tag: char) {} 104 | let (mut sql,mybatis_args) = #func_name_ident(&rbson::Bson::Document(mybatis_arg_map), sql_tag); 105 | driver_type.do_replace_tag(&mut sql); 106 | #call_method 107 | } 108 | } 109 | .into(); 110 | } 111 | 112 | pub(crate) fn filter_args_context_id( 113 | mybatis_name: &str, 114 | fn_arg_name_vec: &Vec, 115 | ) -> proc_macro2::TokenStream { 116 | let mut sql_args_gen = quote! {}; 117 | for item in fn_arg_name_vec { 118 | let item_ident = Ident::new(&item, Span::call_site()); 119 | let item_ident_name = item_ident.to_string(); 120 | if item.eq(&mybatis_name) { 121 | continue; 122 | } 123 | sql_args_gen = quote! { 124 | #sql_args_gen 125 | mybatis_arg_map.insert(#item_ident_name.to_string(),rbson::to_bson(#item_ident).unwrap_or_default()); 126 | }; 127 | } 128 | sql_args_gen 129 | } 130 | -------------------------------------------------------------------------------- /mybatis/README.md: -------------------------------------------------------------------------------- 1 | # Summer MyBatis 2 | 3 | ![Build Status](https://github.com/rust-lang/book/workflows/CI/badge.svg) 4 | 5 | 6 | Summer MyBatis is an ORM framework based on rust language and mybatis framework. It is used to simplify development. Developers do not need to pay attention to complex SQL. They can carry out business development through dynamic SQL dialect. It can save your time. In addition, its speed is very fast. 7 | 8 | 9 | ## Features: 10 | 11 | * Cross platform support (linux, macos, windows) 12 | * No Runtimes,No Garbage Collection 13 | * Powerful and flexible where conditional wrapper. 14 | * Support multiple database drivers (mssql, mysql, postgres, sqlite). 15 | * Support asynchronous operation with tokio. 16 | * Structure automatic serialization and deserialization. 17 | 18 | 19 | ## Getting Started 20 | 21 | * Add mybatis dependency 22 | 23 | ```rust 24 | mybatis = { version = "2.0.3"} 25 | 26 | /// other dependencys 27 | serde = { version = "1", features = ["derive"] } 28 | rbson = "2.0" 29 | tokio = { version = "1.18.2", features = ["full"] } 30 | ``` 31 | 32 | * Example 33 | 34 |
35 | Use #[mybatis_plus] macro data table mapping 36 | 37 | ```rust 38 | use serde::{Serialize, Deserialize}; 39 | 40 | #[mybatis_plus] 41 | #[derive(Debug, Serialize, Deserialize)] 42 | pub struct Books { 43 | pub id: Option, 44 | pub name: Option, 45 | pub types: Option 46 | } 47 | 48 | 49 | #[cfg(test)] 50 | mod tests { 51 | use super::*; 52 | use mybatis::mybatis::Mybatis; 53 | use mybatis::snowflake::SNOWFLAKE; 54 | use mybatis::plus::Mapping; 55 | 56 | #[tokio::test] 57 | async fn save_books() { 58 | let mybatis = Mybatis::new(); 59 | 60 | mybatis.link("mysql://root:passw0rd@localhost:3306/test").await.unwrap(); 61 | 62 | let id = SNOWFLAKE.generate(); 63 | let cat = Books { 64 | id: Some(id.to_string()), 65 | name: Some("《Daughter of the sea》".to_string()), 66 | types: Some("Fairy Tales".to_string()), 67 | }; 68 | 69 | mybatis.save(&cat,&[]).await; 70 | } 71 | } 72 | ``` 73 |
74 | 75 |
76 | If you don't want to use macros, you can create a structure corresponding to the database table and map the method 77 | 78 | ```rust 79 | use mybatis::mybatis_sql::string_util::to_snake_name; 80 | use mybatis::plus::MybatisPlus; 81 | use serde::{Serialize, Deserialize}; 82 | 83 | #[derive(Debug, Serialize, Deserialize)] 84 | pub struct Pets { 85 | pub id: Option, 86 | pub name: Option, 87 | pub birthday: Option, 88 | pub delete_flag: Option, 89 | } 90 | 91 | impl MybatisPlus for Pets { 92 | 93 | fn table_name() -> String { 94 | let type_name = std::any::type_name::(); 95 | let mut name = type_name.to_string(); 96 | let names: Vec<&str> = name.split("::").collect(); 97 | name = names.get(names.len() - 1).unwrap_or(&"").to_string(); 98 | 99 | to_snake_name(&name) 100 | } 101 | 102 | fn table_columns() -> String { 103 | String::from("id,name,birthday,delete_flag") 104 | } 105 | 106 | } 107 | 108 | #[cfg(test)] 109 | mod tests { 110 | use super::*; 111 | use mybatis::mybatis::Mybatis; 112 | use mybatis::snowflake::SNOWFLAKE; 113 | use mybatis::plus::{Skip, Mapping}; 114 | 115 | /// 116 | /// Save a single object 117 | /// 118 | #[tokio::test] 119 | async fn save_pets() { 120 | let mybatis = Mybatis::new(); 121 | 122 | mybatis.link("mysql://root:passw0rd@localhost:3306/test").await.unwrap(); 123 | 124 | let id = SNOWFLAKE.generate(); 125 | let cat = Pets { 126 | id: Some(id.to_string()), 127 | name: Some("Cindy".to_string()), 128 | birthday: Some(mybatis::DateTimeNative::now()), 129 | delete_flag: Some(0), 130 | }; 131 | 132 | mybatis.save(&cat,&[]).await; 133 | } 134 | 135 | /// 136 | /// Query a single object according to the specified field and return Option 137 | /// 138 | #[tokio::test] 139 | async fn query_pet_by_name() { 140 | let mybatis = Mybatis::new(); 141 | 142 | mybatis.link("mysql://root:passw0rd@localhost:3306/test").await.unwrap(); 143 | 144 | let result: Option = mybatis.fetch_by_column("name", &"Cindy").await.unwrap(); 145 | println!("result: {:?}", result); 146 | } 147 | } 148 | ``` 149 | 150 | 151 | ## Contribution 152 | 153 | So far, we have only implemented some flexible conditional wrappers for summer-mybatis. We are developing the mapping of XML files and supporting Oracle database driver. If you have any interests and ideas, please submit to PR or contact me. 154 | 155 | I have been working hard and are looking for various contributions. Look forward to your help! -------------------------------------------------------------------------------- /mybatis-util/src/table_util.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{BTreeMap, HashMap}; 2 | /// Simplifies table construction by relying on the Default trait 3 | /// 4 | /// step1: impl Default 5 | /// #[crud_table] 6 | /// #[derive(Clone, Debug, Default)] 7 | /// BizActivity{ 8 | /// } 9 | /// 10 | /// //step2: make struct 11 | /// let activity = mybatis::make_table!(BizActivity{ 12 | /// id : "12312".to_string(), 13 | /// delete_flag : 1, 14 | /// name: None, 15 | /// }); 16 | #[macro_export] 17 | macro_rules! make_table { 18 | ($t:path{ $($key:ident:$value:expr$(,)?)+ }) => { 19 | { 20 | let mut temp_table_data = <$t>::default(); 21 | $(temp_table_data.$key = $value.into();)+ 22 | temp_table_data 23 | } 24 | } 25 | } 26 | /// take the target Vec member attribute Vec collection 27 | /// vec_ref: a reference to vec, field_name: the field name of the structure 28 | /// 29 | /// need impl Clone or #[derive(Clone, Debug)] 30 | /// for example: 31 | /// struct SysUserRole{ 32 | /// pub role_id:String 33 | /// } 34 | /// let user_roles: Vec; 35 | /// let role_ids = make_table_field_vec!(&user_roles,role_id); // role_ids: Vec 36 | /// 37 | /// 38 | /// 39 | #[allow(unused_macros)] 40 | #[macro_export] 41 | macro_rules! make_table_field_vec { 42 | ($vec_ref:expr,$field_name:ident) => {{ 43 | let mut ids = vec![]; 44 | for item in $vec_ref { 45 | match item.$field_name.as_ref() { 46 | std::option::Option::Some(v) => { 47 | ids.push(v.clone()); 48 | } 49 | _ => {} 50 | } 51 | } 52 | ids 53 | }}; 54 | } 55 | 56 | /// Gets the HashMap collection of member attributes of the target Vec 57 | /// vec_ref: vec reference,field_name: the field name of the structure 58 | /// 59 | /// need impl Clone or #[derive(Clone, Debug)] 60 | /// for example: 61 | /// struct SysUserRole{ 62 | /// pub role_id:String 63 | /// } 64 | /// let user_roles: Vec; 65 | /// let role_ids = make_table_field_map!(&user_roles,role_id); // role_ids: HashMap 66 | /// 67 | /// 68 | /// 69 | #[allow(unused_macros)] 70 | #[macro_export] 71 | macro_rules! make_table_field_map { 72 | ($vec_ref:expr,$field_name:ident) => {{ 73 | let mut ids = std::collections::HashMap::new(); 74 | for item in $vec_ref { 75 | match item.$field_name.as_ref() { 76 | std::option::Option::Some(v) => { 77 | ids.insert(v.clone(), item.clone()); 78 | } 79 | _ => {} 80 | } 81 | } 82 | ids 83 | }}; 84 | } 85 | 86 | /// Gets the HashMap collection of member attributes of the target Vec 87 | /// vec_ref: vec reference,field_name: the field name of the structure 88 | /// 89 | /// need impl Clone or #[derive(Clone, Debug)] 90 | /// for example: 91 | /// struct SysUserRole{ 92 | /// pub role_id:String 93 | /// } 94 | /// let user_roles: Vec; 95 | /// let role_ids = make_table_field_map_btree!(&user_roles,role_id); // role_ids: HashMap 96 | /// 97 | /// 98 | /// 99 | #[allow(unused_macros)] 100 | #[macro_export] 101 | macro_rules! make_table_field_map_btree { 102 | ($vec_ref:expr,$field_name:ident) => {{ 103 | let mut ids = std::collections::BTreeMap::new(); 104 | for item in $vec_ref { 105 | match item.$field_name.as_ref() { 106 | std::option::Option::Some(v) => { 107 | ids.insert(v.clone(), item.clone()); 108 | } 109 | _ => {} 110 | } 111 | } 112 | ids 113 | }}; 114 | } 115 | 116 | #[allow(unused_macros)] 117 | #[macro_export] 118 | macro_rules! as_bson { 119 | ($key:expr) => { 120 | rbson::to_bson($key).unwrap_or_default() 121 | }; 122 | } 123 | 124 | /// Used to simulate enumerations to improve code maintainability. 125 | /// this is return &str data 126 | /// for example: 127 | /// let name=field_name!(BizActivity.id); 128 | /// rb.new_wrapper().eq(field_name!(BizActivity.id),"1") 129 | /// 130 | #[allow(unused_macros)] 131 | #[macro_export] 132 | macro_rules! field_name { 133 | ($t:ident.$field:ident) => {{ 134 | if false { 135 | |a: $t| a.$field; 136 | } 137 | stringify!($field).trim_start_matches("r#") 138 | }}; 139 | } 140 | 141 | /// will create pub fn field_name()->&str method 142 | /// for example: 143 | /// #[crud_table] 144 | /// #[derive(Clone, Debug)] 145 | /// pub struct BizActivity { 146 | /// pub id: Option, 147 | /// pub name: Option, 148 | /// pub delete_flag: Option, 149 | /// } 150 | /// impl_field_name_method!(BizActivity{id,name,delete_flag}); 151 | /// 152 | /// println!("{}",BizActivity::delete_flag()); 153 | #[allow(unused_macros)] 154 | #[macro_export] 155 | macro_rules! impl_field_name_method { 156 | ($target:path{$($field_name:ident$(,)?)+}) => { 157 | impl $target{ 158 | $( 159 | #[inline] 160 | pub fn $field_name()->&'static str{ 161 | if false{ 162 | |a:$target|{a.$field_name}; 163 | } 164 | stringify!($field_name).trim_start_matches("r#") 165 | } 166 | )+ 167 | } 168 | }; 169 | } 170 | --------------------------------------------------------------------------------