use crate::config::{self, CONVENTION_COLUMNS, kw};
use crate::table_dao::table_util;
use crate::utils;
use serde::Serialize;
#[derive(Debug, Default, Clone, Serialize)]
pub struct Entity {
pub name: String,
pub columns: Vec<Column>,
pub comment: String,
}
#[derive(Debug, Default, Clone, Serialize)]
pub struct Column {
pub name: String,
pub nick: String,
pub data_type: String,
pub is_primary_key: bool,
pub is_auto_increment: bool,
pub default_value: Option<String>,
pub is_nullable: bool,
pub comment: String,
}
impl Entity {
fn is_convention(&self, column: &Column) -> bool {
CONVENTION_COLUMNS.contains(&column.name.as_str())
&& config::convention_enabled(&self.name, &column.name)
}
fn has_convention(&self, name: &str) -> bool {
self.columns
.iter()
.any(|it| it.name == name && self.is_convention(it))
}
pub fn has_version(&self) -> bool {
self.has_convention("version")
}
pub fn has_deleted(&self) -> bool {
self.has_convention("deleted")
}
pub fn has_deleted_at(&self) -> bool {
self.has_convention("deleted_at")
}
pub fn has_deleted_by(&self) -> bool {
self.has_convention("deleted_by")
}
fn primary_key_columns(&self) -> impl Iterator<Item = &Column> {
self.columns.iter().filter(|it| it.is_primary_key)
}
pub fn entity_name(&self) -> String {
utils::snake_to_pascal(&self.name, "_")
}
pub fn make_entity_struct(&self) -> String {
let member_src = self
.columns
.iter()
.map(|it| it.make_member_src())
.collect::<Vec<_>>()
.join("");
r#"
#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize, sqlx::FromRow)]
pub struct [NAME] {
[MEMBER]
}
/// 给 结构体 实现 AsRef
impl AsRef<[NAME]> for [NAME] {
#[inline]
fn as_ref(&self) -> &[NAME] {
self
}
}
"#
.replace("[NAME]", self.entity_name().as_str())
.replace("[MEMBER]", member_src.as_str())
.to_string()
}
pub fn make_query_entity_src(&self) -> String {
let mut entity_src = String::new();
entity_src.push_str("#[derive(Default)]\n");
entity_src.push_str(&format!(
"pub struct {}Query {{\n",
utils::snake_to_pascal(&self.name, "_")
));
self.columns.iter().for_each(|it| {
let mut field_type = table_util::db_type_to_rust(&it.data_type, &it.name).to_string();
field_type = format!("QueryModel<{}>", field_type);
entity_src.push_str(&format!(" pub {}: {},\n", it.name, field_type));
});
entity_src.push_str("}\n");
entity_src
}
fn auto_increment_and_primary_column(&self) -> Option<String> {
self.columns.iter().find_map(|column| {
if column.is_auto_increment && column.is_primary_key {
Some(column.name.clone())
} else {
None
}
})
}
pub fn make_insert_func(&self) -> String {
let mut insert_columns: Vec<&str> = Vec::new(); let mut insert_params_replace: Vec<&str> = Vec::new(); let mut insert_params: Vec<String> = Vec::new();
let mut need_now = false; self.columns
.iter()
.filter(|it| {
if it.is_auto_increment && it.is_primary_key {
return false;
}
!(self.is_convention(it)
&& matches!(
it.name.as_str(),
"deleted_at" | "deleted_by" | "deleted" | "version"
))
})
.for_each(|it| {
insert_columns.push(it.name.as_str());
insert_params_replace.push("?");
if self.is_convention(it) {
need_now = true;
insert_params.push("timestamp".to_string());
} else {
insert_params.push(format!("entity.{}", it.name));
}
});
let mut insert_sql = format!(
"{} {} ({}) {} ({})",
kw("INSERT INTO"),
self.name,
insert_columns.join(", "),
kw("VALUES"),
insert_params_replace.join(", ")
);
let fn_template = if let Some(key) = self.auto_increment_and_primary_column() {
insert_sql.push_str(&format!(" {} {}", kw("RETURNING"), key));
r#"
/// 插入数据
pub async fn insert(ctx: &mut sqlx_context::DbContext, entity: impl AsRef<[ENTITY]>) -> Result<i64, sqlx::Error>{
[TIME_CODE]
let entity = entity.as_ref();
let id = sqlx::query_scalar!("[SQL]", [PARAM]).fetch_one(&mut *ctx).await?;
[WRITE_LOG]
Ok(id)
}"#
} else {
r#"
/// 插入数据
pub async fn insert(ctx: &mut sqlx_context::DbContext, entity: impl AsRef<[ENTITY]>) -> Result<(),sqlx::Error>{
[TIME_CODE]
let entity = entity.as_ref();
sqlx::query!("[SQL]", [PARAM]).execute(&mut *ctx).await?;
[WRITE_LOG]
Ok(())
}"#
};
let time_code = if need_now {
"let timestamp = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis() as i64;"
} else {
""
};
fn_template
.replace("[TIME_CODE]", time_code)
.replace("[WRITE_LOG]", &utils::write_log_src(&format!("\"{}\"", insert_sql), &insert_params))
.replace("[SQL]", &insert_sql)
.replace("[PARAM]", &(insert_params.join(", ")))
.replace("[ENTITY]", &utils::snake_to_pascal(&self.name, "_"))
}
pub fn make_select_one_func(&self) -> String {
let mut where_params: Vec<&str> = Vec::new(); let mut func_params: Vec<String> = Vec::new();
let where_columns: Vec<String> = self
.primary_key_columns()
.map(|it| {
where_params.push(it.name.as_str());
func_params.push(format!(
"{}:{}",
it.name,
table_util::db_type_to_rust(&it.data_type, &it.name)
));
format!("{} = ?", it.name)
})
.collect();
if where_columns.is_empty() {
return String::new();
}
let filed_columns: Vec<&str> = self.columns.iter().map(|it| it.name.as_str()).collect();
let select_one_template = r##"
/// 通过主键查询一条数据
pub async fn select_one(ctx: &mut sqlx_context::DbContext, [FUNC_PARAMS]) -> Result<[ENTITY], sqlx::Error>
{
sqlx::query_as!(
[ENTITY],
"[SELECT] [FIELDS] [FROM] [TABLE] [WHERE_KW] [WHERE]",
[PARAM]
).fetch_one(ctx).await
}
"##;
select_one_template
.replace("[FUNC_PARAMS]", &func_params.join(", "))
.replace("[SELECT]", &kw("SELECT"))
.replace("[FROM]", &kw("FROM"))
.replace("[WHERE_KW]", &kw("WHERE"))
.replace("[TABLE]", &self.name)
.replace("[FIELDS]", &filed_columns.join(", "))
.replace("[ENTITY]", &utils::snake_to_pascal(&self.name, "_"))
.replace("[WHERE]", &where_columns.join(&format!(" {} ", kw("AND"))))
.replace("[PARAM]", &where_params.join(", "))
}
pub fn make_select_all_func(&self) -> String {
let mut templates = Vec::new();
if self.has_deleted() {
let select_all_template = r##"
/// 查询所有未删除的数据
pub async fn select_all(ctx: &mut sqlx_context::DbContext) -> Result<Vec<[ENTITY]>, sqlx::Error>
{
sqlx::query_as!([ENTITY], "[SELECT] * [FROM] [TABLE] [WHERE_KW] deleted = 0")
.fetch_all(ctx)
.await
}
"##;
let select_all_include_deleted_template = r##"
/// 查询所有数据,包括已删除的数据
pub async fn select_all_include_deleted(ctx: &mut sqlx_context::DbContext) -> Result<Vec<[ENTITY]>, sqlx::Error>
{
sqlx::query_as!([ENTITY], "[SELECT] * [FROM] [TABLE]")
.fetch_all(ctx)
.await
}
"##;
templates.push(select_all_template);
templates.push(select_all_include_deleted_template);
} else {
templates.push(
r##"
/// 查询所有
pub async fn select_all(ctx: &mut sqlx_context::DbContext) -> Result<Vec<[ENTITY]>, sqlx::Error>
{
sqlx::query_as!([ENTITY], "[SELECT] * [FROM] [TABLE]")
.fetch_all(ctx)
.await
}
"##,
);
}
templates
.iter()
.map(|it| -> String {
it.replace("[SELECT]", &kw("SELECT"))
.replace("[FROM]", &kw("FROM"))
.replace("[WHERE_KW]", &kw("WHERE"))
.replace("[TABLE]", &self.name)
.replace("[ENTITY]", &utils::snake_to_pascal(&self.name, "_"))
})
.collect::<Vec<_>>()
.join("\n")
}
pub fn make_update_func(&self) -> String {
let mut update_columns: Vec<String> = Vec::new(); let mut update_params: Vec<String> = Vec::new(); let mut where_columns: Vec<String> = Vec::new();
let has_version = self.has_version();
if has_version {
update_columns.push("version = version + 1".to_string());
}
let mut timestamp = String::new();
self.columns
.iter()
.filter(|it| {
if it.is_primary_key {
return false;
}
!(self.is_convention(it)
&& matches!(
it.name.as_str(),
"created_at" | "deleted_at" | "deleted" | "version"
))
})
.for_each(|it| {
update_columns.push(format!("{} = ?", it.name));
if it.name == "updated_at" && self.is_convention(it) {
timestamp = "let timestamp = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis() as i64;".to_string();
update_params.push("timestamp".to_string());
} else {
update_params.push(format!("entity.{}", it.name));
}
});
self.columns
.iter()
.filter(|it| it.is_primary_key)
.for_each(|it| {
where_columns.push(format!("{} = ?", it.name));
update_params.push(format!("entity.{}", it.name));
});
if has_version {
where_columns.push("version = ?".to_string());
update_params.push("entity.version".to_string());
}
if self.has_deleted() {
where_columns.push("deleted = 0".to_string());
}
if where_columns.is_empty() || update_columns.is_empty() {
return String::new();
}
let update_sql = format!(
"{} {} {} {} {} {}",
kw("UPDATE"),
self.name,
kw("SET"),
update_columns.join(", "),
kw("WHERE"),
where_columns.join(&format!(" {} ", kw("AND")))
);
r##"
/// 更新数据
pub async fn update(ctx: &mut sqlx_context::DbContext, entity: impl AsRef<[ENTITY]>) -> Result<u64, sqlx::Error>
{
[TIMESTAMP]
let entity = entity.as_ref();
let rs = sqlx::query!(
"[SQL]",
[PARAM]
).execute(&mut *ctx).await?;
[WRITE_LOG]
let count = rs.rows_affected();
Ok(count)
}
"##
.replace("[TIMESTAMP]", timestamp.as_str())
.replace("[WRITE_LOG]", &utils::write_log_src(&format!("\"{}\"", update_sql), &update_params))
.replace("[SQL]", &update_sql)
.replace("[PARAM]", &(update_params.join(", ")))
.replace("[ENTITY]", self.entity_name().as_str())
}
pub fn make_set_delete_func(&self) -> String {
self.make_logic_delete_func("set_delete", true)
}
pub fn make_set_delete_ignore_version_func(&self) -> String {
self.make_logic_delete_func("set_delete_ignone_version", false)
}
fn make_logic_delete_func(&self, fn_name: &str, check_version: bool) -> String {
if !self.has_deleted() {
return String::new();
}
let check_version = check_version && self.has_version();
let mut where_columns = Vec::new(); let mut sql_params: Vec<String> = Vec::new(); let mut func_params: Vec<String> = Vec::new(); let mut update_fields = vec!["deleted = 1"]; self.primary_key_columns().for_each(|it| {
func_params.push(format!(
"{}: {}",
it.name,
table_util::db_type_to_rust(&it.data_type, &it.name)
));
where_columns.push(format!("{} = ?", it.name));
});
if where_columns.is_empty() {
return String::new();
}
if check_version {
func_params.push("version: i64".to_string());
where_columns.push("version = ?".to_string());
}
if self.has_version() {
update_fields.push("version = version + 1");
}
let mut timestamp = String::new();
if self.has_deleted_at() {
timestamp = "let timestamp = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis() as i64;".to_string();
update_fields.push("deleted_at = ?");
}
if self.has_deleted_by() {
update_fields.push("deleted_by = ?");
func_params.push("deleted_by: String".to_string());
}
where_columns.push("deleted = 0".to_string());
if self.has_deleted_at() {
sql_params.push("timestamp".to_string());
}
if self.has_deleted_by() {
sql_params.push("deleted_by".to_string());
}
self.primary_key_columns().for_each(|it| {
sql_params.push(it.name.clone());
});
if check_version {
sql_params.push("version".to_string());
}
let delete_sql = format!(
"{} {} {} {} {} {}",
kw("UPDATE"),
self.name,
kw("SET"),
update_fields.join(", "),
kw("WHERE"),
where_columns.join(&format!(" {} ", kw("AND")))
);
r##"
/// 逻辑删除数据
pub async fn [FN_NAME](
ctx: &mut sqlx_context::DbContext,
[FUNC_PARAMS]
) -> Result<u64, sqlx::Error>
{
[TIMESTAMP]
let rs = sqlx::query!("[SQL]", [PARAM])
.execute(&mut *ctx)
.await?;
[WRITE_LOG]
let count = rs.rows_affected();
Ok(count)
}
"##
.replace("[FN_NAME]", fn_name)
.replace("[TIMESTAMP]", ×tamp)
.replace("[WRITE_LOG]", &utils::write_log_src(&format!("\"{}\"", delete_sql), &sql_params))
.replace("[SQL]", &delete_sql)
.replace("[PARAM]", &sql_params.join(", "))
.replace("[FUNC_PARAMS]", &func_params.join(", "))
}
pub fn make_delete_func(&self) -> String {
let mut where_columns = Vec::new(); let mut sql_params: Vec<String> = Vec::new(); let mut func_params: Vec<String> = Vec::new(); self.primary_key_columns().for_each(|it| {
sql_params.push(it.name.clone());
func_params.push(format!(
"{}: {}",
it.name,
table_util::db_type_to_rust(&it.data_type, &it.name)
));
where_columns.push(format!("{} = ?", it.name));
});
if where_columns.is_empty() {
return String::new();
}
let delete_sql = format!(
"{} {} {} {}",
kw("DELETE FROM"),
self.name,
kw("WHERE"),
where_columns.join(&format!(" {} ", kw("AND")))
);
r##"
/// 物理删除数据
pub async fn delete(ctx: &mut sqlx_context::DbContext, [FUNC_PARAMS]) -> Result<u64, sqlx::Error>
{
let rs = sqlx::query!("[SQL]", [PARAM])
.execute(&mut *ctx)
.await?;
[WRITE_LOG]
let count = rs.rows_affected();
Ok(count)
}
"##
.replace("[WRITE_LOG]", &utils::write_log_src(&format!("\"{}\"", delete_sql), &sql_params))
.replace("[SQL]", &delete_sql)
.replace("[PARAM]", &sql_params.join(", "))
.replace("[FUNC_PARAMS]", &func_params.join(", "))
}
}
impl Column {
pub fn make_member_src(&self) -> String {
let mut field_type = table_util::db_type_to_rust(&self.data_type, &self.name).to_string();
if self.is_nullable {
field_type = format!(": Option<{}>", field_type);
} else {
field_type = format!(": {}", field_type);
}
let mut comment = self.comment.clone();
if !comment.is_empty() {
comment = format!("/// {}", comment)
}
format!("{}\n pub {} {},\n", comment, self.name, field_type)
}
}