use crate::dsl_dao::model::dsl_block::{DslBlock, DslController};
use crate::dsl_dao::model::dsl_param::DslParam;
use crate::table_dao::model::{CrudType, table};
use crate::table_dao::table_util;
use crate::utils;
use sqlx::{SqliteConnection, SqlitePool};
use std::collections::{HashMap, HashSet};
#[derive(Debug, Default, Clone, serde::Serialize)]
pub struct DslFunction {
pub file_name: String,
pub comment: Vec<String>,
pub name: String,
pub return_type: String,
pub return_list: bool,
pub return_option: bool,
pub is_page: bool,
pub params: Vec<DslParam>,
pub param_type: Option<String>,
pub block_list: Vec<DslBlock>,
}
impl DslFunction {
pub fn has_if_condition(&self) -> bool {
self.block_list.iter().any(|it| it.controller.is_some())
}
pub fn no_control_sql(&self) -> String {
self.block_list
.iter()
.filter_map(|it| {
if it.controller.is_some() {
return None;
}
Some(it.sql.clone())
})
.collect::<Vec<String>>()
.join("")
}
pub fn crud_type(&self) -> CrudType {
let sql = self.no_control_sql().trim().to_uppercase();
if sql.starts_with("SELECT") {
CrudType::Read
} else if sql.starts_with("INSERT") {
CrudType::Create
} else if sql.starts_with("UPDATE") {
CrudType::Update
} else if sql.starts_with("DELETE") {
CrudType::Delete
} else {
CrudType::Unknown
}
}
pub async fn result_entity(&self, conn: &SqlitePool) -> table::Entity {
let sql = self.no_control_sql();
let columns = table_util::get_column_from_sql(conn, &sql).await;
table::Entity {
name: self.return_type.clone(),
comment: self.comment.join(","),
columns,
..Default::default()
}
}
pub fn param_entity(&self) -> table::Entity {
let Some(param_type) = &self.param_type else {
panic!("生成Dsl函数参数时,param_type必填")
};
let columns = self
.params
.iter()
.map(|it| table::Column {
name: it.name.clone(),
data_type: it.rust_type().to_string(),
comment: it.comment.clone(),
..Default::default()
})
.collect();
table::Entity {
name: param_type.to_string(),
columns,
comment: self.comment.join(","),
..Default::default()
}
}
pub fn method_param_src(&self) -> String {
if let Some(param_type) = &self.param_type {
format!("param: {}", param_type)
} else {
self.params.iter().fold(String::new(), |mut pre, it| {
let rust_type = it.rust_type();
let mut rust_type = rust_type.as_str();
if rust_type == "String" {
rust_type = "impl AsRef<str>";
}
pre.push_str(it.name.as_str());
pre.push_str(": ");
pre.push_str(rust_type);
pre.push_str(", ");
pre
})
}
}
pub fn method_param_as_ref_src(&self) -> String {
if self.param_type.is_some() {
"".to_string()
} else {
self.params
.iter()
.filter_map(|it| {
if it.rust_type() == "String" {
return Some(format!("let {} = {}.as_ref();\n", it.name, it.name));
}
None
})
.collect::<Vec<String>>()
.join("")
}
}
pub fn concat_sql_src(&self, write_log: bool) -> String {
let need_param = self.param_type.is_some();
self.block_list
.iter()
.map(|sql_item| sql_item.block_src(need_param, write_log))
.collect::<Vec<_>>()
.join("\n")
}
pub fn native_sql_and_arg_list(&self) -> (String, Vec<String>) {
let (native_sql, args) = table_util::parse_sql_args(self.no_control_sql().as_str());
let var_name = if self.param_type.is_some() {
"param."
} else {
""
};
let args = args.iter().map(|it| format!("{}{}", var_name, it)).collect();
(native_sql.trim().to_string(), args)
}
pub fn native_sql_and_args(&self) -> (String, String) {
let (native_sql, args) = table_util::parse_sql_args(self.no_control_sql().as_str());
let var_name = if self.param_type.is_some() {
"param."
} else {
""
};
let exec_args = args
.iter()
.map(|it| format!("{}{}", var_name, it))
.collect::<Vec<_>>()
.join(", ");
(native_sql, exec_args)
}
pub async fn fix(&self, conn: &SqlitePool) -> DslFunction {
let mut dsl = self.fix_params(conn).await;
if dsl.crud_type() == CrudType::Read && dsl.return_type == "" {
let columns =
table_util::get_column_from_sql(conn, self.no_control_sql().as_str()).await;
if columns.len() == 1 {
let first = &columns[0];
dsl.return_type = table_util::db_type_to_rust(&first.data_type, &first.name);
dsl.return_option = first.is_nullable;
} else if columns.len() > 1 {
dsl.return_type = format!("{}Temp", utils::snake_to_pascal(&dsl.name, "_"))
}
}
if dsl.param_type.is_none() && dsl.params.len() > 4 {
dsl.param_type = Some(format!("{}Param", utils::snake_to_pascal(&dsl.name, "_")));
}
if let Some(param_type) = dsl.param_type {
dsl.param_type = Some(utils::snake_to_pascal(param_type, "_"));
}
if dsl.name.is_empty() {
dsl.name = format!("untitled_{}", dsl.no_control_sql().len());
}
dsl
}
pub async fn fix_params(&self, conn: &SqlitePool) -> DslFunction {
let result_column_set = if self.crud_type() == CrudType::Read {
self.result_entity(conn)
.await
.columns
.iter()
.fold(HashMap::new(), |mut pre, it| {
pre.insert(it.name.clone(), it.clone());
pre
})
} else {
HashMap::new()
};
let mut sql_args_set = Vec::new();
self.block_list.iter().for_each(|it| {
let (_, args) = it.native_sql_and_args();
args.into_iter().for_each(|arg| {
if let Some(DslController::Each(each)) = &it.controller {
if arg == each.item || arg.starts_with(format!("{}.", &each.item).as_str()) {
return;
}
}
if !sql_args_set.contains(&arg) {
sql_args_set.push(arg);
}
});
});
let mut current_param_set = self.params.iter().fold(HashSet::new(), |mut pre, it| {
pre.insert(it.name.clone());
pre
});
let mut params = self.params.clone();
sql_args_set.iter().for_each(|it| {
if current_param_set.contains(it) {
return;
}
if let Some(column) = result_column_set.get(it) {
params.push(DslParam {
name: it.clone(),
ty: table_util::db_type_to_rust(&column.data_type, &column.name),
is_option: false,
..Default::default()
});
current_param_set.insert(it.clone());
}
});
sql_args_set.iter().for_each(|it| {
if !current_param_set.contains(it) {
let ty = table_util::schema_column_type(it).unwrap_or_else(|| if it == "id" {
"i64"
} else if it.to_lowercase().ends_with("date") {
"chrono::NaiveDateTime"
} else if it.to_lowercase().starts_with("is") {
"bool"
} else {
"String"
}
.to_string());
params.push(DslParam {
name: it.clone(),
ty,
is_option: false,
..Default::default()
})
}
});
let mut fix_dsl = self.clone();
fix_dsl.params = params;
fix_dsl
}
}