use std::{env, fs};
use std::fs::{create_dir_all, File};
use std::io::Write;
use std::path::{Path, PathBuf};
#[cfg(any(feature = "mssql", feature = "sqlite", feature = "mysql"))]
use br_db::{Mode, Table};
use br_fields::Field;
use json::{array, JsonValue, object};
pub mod test;
use crate::test::Test;
use crate::test::test::table::TestTestTable;
use crate::test::test::TestTest;
use br_db::Db;
#[cfg(any(feature = "default", feature = "cache"))]
use br_cache::Cache;
#[cfg(any(feature = "default", feature = "kafka"))]
use br_kafka::Kafka;
use lazy_static::lazy_static;
use std::sync::Mutex;
use std::collections::HashMap;
use log::info;
#[derive(Clone)]
pub struct Tools {
pub db: Db,
#[cfg(any(feature = "default", feature = "cache"))]
pub cache: Cache,
#[cfg(any(feature = "default", feature = "kafka"))]
pub kafka: Kafka,
#[cfg(any(feature = "default", feature = "email-stmp"))]
pub email_stmp: br_email::smtp::Smtp,
#[cfg(any(feature = "default", feature = "email-pop3"))]
pub email_pop3: br_email::pop3::Pop3,
}
impl Tools {
pub fn new() -> Self {
Self {
db: Db::None,
#[cfg(any(feature = "default", feature = "cache"))]
cache: Cache::connect(object! {}),
#[cfg(any(feature = "default", feature = "kafka"))]
kafka: Kafka::connect(object! {}),
#[cfg(any(feature = "default", feature = "email-stmp"))]
email_stmp: br_email::smtp::Smtp::connect(object! {}),
#[cfg(any(feature = "default", feature = "email-pop3"))]
email_pop3: br_email::pop3::Pop3::new(object! {}),
}
}
pub fn set_db(&mut self, config_path: &str) -> &mut Self {
let conf = fs::read_to_string(config_path.clone()).unwrap();
match json::parse(conf.as_str().clone()) {
Ok(config) => {
self.db = Db::new(config);
PLUGIN_TOOLS.lock().unwrap().insert("tools".to_string(), self.clone());
}
Err(e) => {
info!("数据库配置文件错误: {}",e.to_string());
}
}
self
}
pub fn set_db_json(&mut self, config: JsonValue) -> &mut Self {
self.db = Db::new(config);
PLUGIN_TOOLS.lock().unwrap().insert("tools".to_string(), self.clone());
self
}
#[cfg(any(feature = "default", feature = "cache"))]
pub fn set_cache(&mut self, config_path: &str) -> &mut Self {
let conf = fs::read_to_string(config_path.clone()).unwrap();
match json::parse(conf.as_str().clone()) {
Ok(config) => {
self.cache = Cache::connect(config);
PLUGIN_TOOLS.lock().unwrap().insert("tools".to_string(), self.clone());
}
Err(e) => {
info!("缓存配置文件错误: {}",e.to_string());
}
}
self
}
#[cfg(any(feature = "default", feature = "kafka"))]
pub fn set_kafka(&mut self, config_path: &str) -> &mut Self {
let conf = fs::read_to_string(config_path.clone()).unwrap();
match json::parse(conf.as_str().clone()) {
Ok(config) => {
self.kafka = Kafka::connect(config);
PLUGIN_TOOLS.lock().unwrap().insert("tools".to_string(), self.clone());
}
Err(e) => {
info!("kafka配置文件错误: {}",e.to_string());
}
}
self
}
#[cfg(any(feature = "default", feature = "email-stmp"))]
pub fn set_email_stmp(&mut self, config_path: &str) -> &mut Self {
let conf = fs::read_to_string(config_path.clone()).unwrap();
match json::parse(conf.as_str().clone()) {
Ok(config) => {
self.email_stmp = br_email::smtp::Smtp::connect(config.clone());
PLUGIN_TOOLS.lock().unwrap().insert("tools".to_string(), self.clone());
}
Err(e) => {
info!("kafka配置文件错误: {}",e.to_string());
}
}
self
}
#[cfg(any(feature = "default", feature = "email-pop3"))]
pub fn set_email_pop3(&mut self, config_path: &str) -> &mut Self {
let conf = fs::read_to_string(config_path.clone()).unwrap();
match json::parse(conf.as_str().clone()) {
Ok(config) => {
self.email_pop3 = br_email::pop3::Pop3::new(config.clone());
PLUGIN_TOOLS.lock().unwrap().insert("tools".to_string(), self.clone());
}
Err(e) => {
info!("kafka配置文件错误: {}",e.to_string());
}
}
self
}
}
lazy_static! {
pub static ref PLUGIN_TOOLS: Mutex<HashMap<String,Tools>> =Mutex::new(HashMap::new());
}
pub fn plugins(name: &str) -> Box<dyn Plugin> {
match name {
_ => Box::new(Test {})
}
}
pub fn models(name: &str) -> Box<dyn Model> {
match name {
_ => Box::new(TestTest {})
}
}
pub fn actions(name: &str) -> Box<dyn Action> {
match name {
_ => Box::new(TestTestTable { model: TestTest {} })
}
}
pub fn plugin_create(path: &str, plugin: &str, plugin_title: &str, model: &str, model_title: &str, action: &str, action_title: &str) {
let root_path = PathBuf::from(path);
let plugin_path = root_path.join("plugin");
let plugin_path = plugin_path.join(plugin);
create_dir_all(plugin_path.as_os_str()).expect("创建目录失败");
let plugin_mod_path = plugin_path.join("mod.rs");
if !plugin_mod_path.is_file() {
let temp_plugin_mod_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let temp_plugin_mod_path = temp_plugin_mod_path.join("temp").join("plugin");
let temp_plugin_mod_data = fs::read_to_string(temp_plugin_mod_path).unwrap();
let plugin_1 = plugin[0..=0].to_uppercase();
let plugin_2 = plugin[1..].to_lowercase();
let temp_plugin_mod_data = temp_plugin_mod_data.replace("{{plugin}}", &*format!("{}{}", plugin_1, plugin_2));
let temp_plugin_mod_data = temp_plugin_mod_data.replace("{{title}}", plugin_title);
fs::write(plugin_mod_path, temp_plugin_mod_data).expect("写入插件mod错误");
}
if model != "" {
let model_path = plugin_path.join(model);
create_dir_all(model_path.as_os_str()).expect("创建模型目录失败");
let plugin_d = {
let t: Vec<String> = plugin.split("_").map(|x| {
let x1 = x[0..=0].to_uppercase();
let x2 = x[1..].to_lowercase();
format!("{}{}", x1.clone(), x2.clone())
}).collect();
t.join("")
};
let model_d = {
let t: Vec<String> = model.split("_").map(|x| {
let x1 = x[0..=0].to_uppercase();
let x2 = x[1..].to_lowercase();
format!("{}{}", x1.clone(), x2.clone())
}).collect();
t.join("")
};
let model_mod_path = model_path.join("mod.rs");
if !model_mod_path.is_file() {
let temp_plugin_mod_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let temp_model_mod_path = temp_plugin_mod_path.join("temp").join("model");
let mut temp_model_mod_data = fs::read_to_string(temp_model_mod_path).unwrap();
temp_model_mod_data = temp_model_mod_data.replace("{{model}}", &*format!("{}{}", plugin_d, model_d));
temp_model_mod_data = temp_model_mod_data.replace("{{plugin_model}}", &*format!("{}_{}", plugin, model));
let temp_model_mod_data = temp_model_mod_data.replace("{{title}}", model_title);
fs::write(model_mod_path, temp_model_mod_data).expect("写入模型mod错误");
}
if action != "" {
let action_path = model_path.join(format!("{}.rs", action));
if !action_path.is_file() {
let temp_action_mod_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let temp_action_mod_path = {
match action {
"table" => {
temp_action_mod_path.join("temp").join("action_table")
}
"add" => {
temp_action_mod_path.join("temp").join("action_add")
}
"delete" => {
temp_action_mod_path.join("temp").join("action_delete")
}
"put" => {
temp_action_mod_path.join("temp").join("action_put")
}
"select" => {
temp_action_mod_path.join("temp").join("action_select")
}
"get" => {
temp_action_mod_path.join("temp").join("action_get")
}
_ => {
temp_action_mod_path.join("temp").join("action")
}
}
};
let temp_action_mod_data = fs::read_to_string(temp_action_mod_path).unwrap();
let action_d = {
let t: Vec<String> = action.split("_").map(|x| {
let x1 = x[0..=0].to_uppercase();
let x2 = x[1..].to_lowercase();
format!("{}{}", x1.clone(), x2.clone())
}).collect();
t.join("")
};
let temp_action_mod_data = temp_action_mod_data.replace("{{action}}", &*format!("{}{}{}", plugin_d, model_d, action_d));
let temp_action_mod_data = temp_action_mod_data.replace("{{api}}", &*format!("{}.{}.{}", plugin, model, action));
let temp_action_mod_data = temp_action_mod_data.replace("{{model}}", &*format!("{}{}", plugin_d, model_d));
let temp_action_mod_data = temp_action_mod_data.replace("{{model_a}}", &*format!("{}", model));
let temp_action_mod_data = temp_action_mod_data.replace("{{plugin}}", &*format!("{}", plugin));
let temp_action_mod_data = temp_action_mod_data.replace("{{title}}", action_title);
fs::write(action_path, temp_action_mod_data).expect("写入动作文件错误");
}
}
}
}
pub trait Plugin {
fn model(&mut self, name: &str) -> Box<dyn Model>;
fn title(&mut self) -> &'static str;
fn describe(&mut self) -> &'static str { return ""; }
}
pub trait Model {
fn version(&mut self) -> &'static str {
"0.0.1"
}
fn table(&mut self) -> &'static str;
fn title(&mut self) -> &'static str;
fn describe(&mut self) -> &'static str { return ""; }
fn fields(&mut self) -> JsonValue;
fn unique(&mut self) -> Vec<&'static str> {
return vec![];
}
fn index(&mut self) -> Vec<Vec<&'static str>> {
return vec![];
}
fn primary_key(&mut self) -> &'static str {
return "id";
}
fn auto(&mut self) -> bool {
return false;
}
fn json(&mut self) -> Table {
Table {
version: self.version().parse().unwrap(),
table: self.table().parse().unwrap(),
title: self.title().parse().unwrap(),
primary_key: self.primary_key().parse().unwrap(),
auto: self.auto(),
unique: self.unique().iter().map(|x| x.to_string()).collect(),
index: self.index().iter().map(|x| x.iter().map(|y| y.to_string()).collect()).collect(),
fields: self.fields(),
}
}
fn create_json_file(&mut self, path: &str) -> bool {
let json = self.json();
let o = Path::new(path);
if !o.is_dir() {
fs::create_dir(path).unwrap();
}
env::set_current_dir(path).unwrap();
let dir = env::current_dir().unwrap();
let version = self.version();
let version = version.replace(".", "_");
let dir = dir.join(format!("{}_{}.json", self.table(), version));
let mut f = File::create(dir.to_str().unwrap()).unwrap();
match f.write_all(json.to_string().as_bytes()) {
Ok(_) => true,
Err(_) => false
}
}
fn action(&mut self, name: &str) -> Box<dyn Action>;
fn table_fields(&mut self) -> JsonValue {
let mut params = object! {};
params["load"] = br_fields::select::Radio::new(false, "load", "加载内容", vec!["col", "data"], "data").field();
params["page"] = br_fields::int::Int::new(false, "page", "页数", 15, 1).field();
params["limit"] = br_fields::int::Int::new(false, "limit", "记录数", 10, 25).field();
params["where"] = br_fields::text::Json::new(false, "where", "条件", object! {}).field();
params["filter"] = br_fields::text::Text::new(false, "filter", "模糊搜索", "").field();
params["order"] = br_fields::text::Json::new(false, "order", "排序", array![]).field();
return params;
}
fn tools(&mut self) -> Tools {
let db = PLUGIN_TOOLS.lock().unwrap();
let db = db.get("tools").unwrap().clone();
db
}
fn tables(&mut self, _header: JsonValue, request: JsonValue, fields: Vec<&str>, query_fields: Vec<&str>, filter_fields: Vec<&str>) -> JsonValue {
let load = request["load"].as_str().unwrap();
let page = request["page"].as_i32().unwrap();
let limit = request["limit"].as_i32().unwrap();
let wheres = request["where"].clone();
let filter = request["filter"].to_string();
let order = request["order"].clone();
let columns = self.columns(fields.clone());
let mut fields_json = vec![];
for field in columns.members() {
match field["mode"].as_str().unwrap() {
"json" => {
fields_json.push(field["name"].as_str().unwrap());
}
_ => {}
}
}
let mut data = {
let mut db = self.tools().db;
let db = db.table(self.table());
if filter_fields.len() > 0 && filter != "" {
db.where_or(filter_fields.join("|").as_str(), "like", format!("%{}%", filter).into());
}
if fields.len() > 0 {
db.field(fields.join(",").as_str().clone());
}
for item in wheres.members() {
db.where_and(item[0].as_str().unwrap(), item[1].as_str().unwrap(), item[2].clone());
}
if order.len() > 0 {
db.order(order[0].as_str().unwrap(), order[1].as_bool().unwrap());
}
if fields_json.len() > 0 {
db.json(&*fields_json.join(","));
}
db.page(page.clone(), limit.clone()).select()
};
let total = {
let mut db = self.tools().db;
let db = db.table(self.table());
if filter_fields.len() > 0 && filter != "" {
db.where_or(filter_fields.join("|").as_str(), "like", format!("%{}%", filter).into());
}
for item in wheres.members() {
db.where_and(item[0].as_str().unwrap(), item[1].as_str().unwrap(), item[2].clone());
}
db.count().as_i32().unwrap()
};
let mut fields_table = false;
let mut fields_list = object! {};
for field in columns.members() {
fields_list[field["field"].as_str().unwrap()] = field.clone();
match field["mode"].as_str().unwrap() {
"table" | "file" => {
fields_table = true;
}
_ => {}
}
}
if fields_table {
let mut fields_table_ids = object! {};
for item in data.members_mut() {
for (key, field) in fields_list.entries() {
match field["mode"].as_str().unwrap() {
"table" => {
let id = item[key].clone();
if id.is_empty() {
continue;
}
if fields_table_ids[id.as_str().unwrap()].is_null() {
let table = field["table"].as_str().unwrap();
let field: Vec<&str> = field["fields"].members().map(|x| x.as_str().unwrap()).collect();
let find = self.tools().db.table(table)
.where_and("id", "=", id.clone())
.field(format!("id,{}", field.join(",")).as_str().clone())
.find();
let mut row = object! {};
if !find.is_empty() {
row["id"] = find["id"].clone();
for field in field.iter() {
if row["value"].is_null() {
row["value"] = format!("{}", find[field.clone()]).into();
} else {
row["value"] = format!("{} | {}", row["value"], find[field.clone()]).into();
}
}
fields_table_ids[id.as_str().unwrap()] = row;
} else {
fields_table_ids[id.as_str().unwrap()] = object! {};
}
}
item[key] = fields_table_ids[id.as_str().unwrap()].clone();
}
"file" => {
let id = item[key].clone();
if id.is_empty() {
item[key] = array![];
continue;
}
let files = self.tools().db.table("file_file")
.where_and("id", "in", id.clone())
.field("id,mode,url,type,encrypt")
.select();
item[key] = files;
}
_ => {}
}
}
}
}
let mut table = object! {};
table["total_page"] = JsonValue::from((total as f64 / limit as f64).ceil() as i64);
table["total_data"] = total.into();
table["data"] = data.clone();
match load {
"col" => {
table["col"] = columns.clone();
table["query_fields"] = self.query_fields(query_fields.clone());
table["filter_title"] = self.filter_title(filter_fields.clone()).into();
}
_ => {}
}
table
}
fn columns(&mut self, fields: Vec<&str>) -> JsonValue {
let columns = self.fields();
let mut data = array![];
for (key, field) in columns.entries() {
if fields.contains(&key) || fields.len() == 0 {
let mut row = field.clone();
row["name"] = field["field"].clone();
row["label"] = field["title"].clone();
row["align"] = "center".into();
row["sortable"] = match field["mode"].as_str().unwrap() {
"int" | "float" | "switch" | "datetime" | "date" | "time" | "year" | "timestamp" => {
true.into()
}
_ => {
false.into()
}
};
data.push(row.clone()).unwrap();
}
}
data
}
fn query_fields(&mut self, fields: Vec<&str>) -> JsonValue {
let columns = self.fields();
let mut data = array![];
for (key, field) in columns.entries() {
if fields.contains(&key) {
let mut row = field.clone();
row["require"] = JsonValue::from(false);
data.push(row.clone()).unwrap();
}
}
data
}
fn filter_title(&mut self, fields: Vec<&str>) -> String {
let columns = self.fields();
let mut data = vec![];
for (key, field) in columns.entries() {
if fields.contains(&key) {
data.push(field["title"].as_str().unwrap());
}
}
format!("搜索 {}", data.join(" "))
}
fn btn_data(&mut self, title: &str, mut action: Box<dyn Action>, mode: BtnMode, color: BtnColor, match_condition: Vec<Vec<&str>>) -> JsonValue {
let mut btn = object! {};
if title.is_empty() {
btn["title"] = action.title().into();
} else {
btn["title"] = title.into();
}
btn["api"] = action.name().into();
let mut params = array![];
for (_, item) in action.params().entries() {
params.push(item.clone()).unwrap();
}
btn["params"] = JsonValue::from(params);
btn["color"] = JsonValue::from(color.from());
btn["mode"] = JsonValue::from(mode.from());
btn["match_condition"] = match_condition.into();
return btn;
}
fn btn_path_data(&mut self, title: &str, path: &str, color: BtnColor, match_condition: Vec<Vec<&str>>) -> JsonValue {
let mut btn = object! {};
btn["title"] = title.into();
btn["path"] = JsonValue::from(path);
btn["color"] = JsonValue::from(color.from());
btn["mode"] = JsonValue::from("path");
btn["match_condition"] = match_condition.into();
return btn;
}
fn table_select_fields(&mut self) -> JsonValue {
let mut fields = object! {};
fields[self.primary_key()] = br_fields::str::Key::new(true, self.primary_key(), "ID", 20).field();
fields["filter"] = br_fields::text::Text::new(true, "filter", "模糊搜索", "").field();
return fields;
}
fn table_select(&mut self, request: JsonValue, fields: Vec<&str>, filter: Vec<Vec<&str>>) -> JsonValue {
let id = request["id"].as_str().unwrap();
let wheres = request["filter"].as_str().unwrap();
let mut db = self.tools().db;
let db = db.table(self.table());
db.field(format!("id,{}", fields.join(",")).as_str().clone());
for item in filter.iter() {
db.where_and(item[0], item[1], item[2].into());
}
if wheres != "" {
if id != "" {
db.where_or("id", "=", id.into());
}
db.where_or(fields.join("|").as_str(), "like", format!("%{}%", wheres).into());
} else {
if id != "" {
db.where_or("id", "=", id.into()).where_or("id", "!=", id.into());
}
}
let data = db.select();
let mut list = array![];
for item in data.members() {
let mut row = object! {};
row["id"] = item["id"].clone();
for field in fields.iter() {
if row["value"].is_null() {
row["value"] = format!("{}", item[field.clone()]).into();
} else {
row["value"] = format!("{} | {}", row["value"], item[field.clone()]).into();
}
}
list.push(row).unwrap();
}
list
}
}
pub enum BtnColor {
Red,
Blue,
Yellow,
}
impl BtnColor {
fn from(self) -> &'static str {
match self {
BtnColor::Red => "red",
BtnColor::Blue => "blue",
BtnColor::Yellow => "yellow",
}
}
}
pub enum BtnMode {
Form,
Url,
Api,
Download,
Path,
}
impl BtnMode {
fn from(self) -> &'static str {
match self {
BtnMode::Form => "form",
BtnMode::Url => "url",
BtnMode::Api => "api",
BtnMode::Download => "download",
BtnMode::Path => "path",
}
}
}
pub trait Action {
fn title(&mut self) -> &'static str;
fn name(&mut self) -> &'static str;
fn token(&mut self) -> bool { true }
fn describe(&mut self) -> &'static str { return ""; }
fn public(&mut self) -> bool { true }
fn auth(&mut self) -> bool { true }
fn interface_type(&mut self) -> &'static str {
""
}
fn dependent(&mut self) -> Vec<&'static str> {
return vec![];
}
fn params(&mut self) -> JsonValue { object! {} }
fn _check(&mut self, mut request: JsonValue) -> (bool, String, JsonValue) {
let params = self.params();
let mut new_request = object! {};
for (key, field) in params.entries() {
if request[key].is_null() && field["require"].as_bool().unwrap() {
return (false, format!("缺少参数 {}:{}", key, field["title"]), request);
}
if request[key].is_empty() && field["require"].as_bool().unwrap() {
request[key] = field["def"].clone().into();
}
if request[key].is_null() && !field["require"].as_bool().unwrap() {
request[key] = field["def"].clone().into();
}
if request[key] == JsonValue::from("null") {
request[key] = field["def"].clone().into();
}
new_request[key] = request[key].clone();
}
return (true, "验证通过".to_string(), new_request);
}
fn run(&mut self, header: JsonValue, request: JsonValue) -> Response {
let (state, msg, request) = self._check(request.clone());
if !state {
return self.fail(&*msg);
}
return self.index(header.clone(), request.clone());
}
fn index(&mut self, header: JsonValue, request: JsonValue) -> Response;
fn success(&mut self, data: JsonValue, msg: &str) -> Response {
Response::success(data, msg)
}
fn fail(&mut self, msg: &str) -> Response {
Response::fail(msg)
}
fn login(&mut self, msg: &str) -> Response {
Response::login(msg)
}
fn download(&mut self, filename: &str) -> Response {
Response::download(filename)
}
fn redirect(&mut self, url: &str) -> Response {
Response::redirect(url)
}
fn tools(&mut self) -> Tools {
PLUGIN_TOOLS.lock().unwrap().get("tools").unwrap().clone()
}
}
#[derive(Clone, Debug)]
pub struct Response(i32, &'static str, JsonValue);
impl Response {
pub fn get_code(self) -> i32 {
self.0
}
pub fn get_msg(self) -> &'static str {
self.1
}
pub fn get_data(self) -> JsonValue {
self.2
}
pub fn into(self) -> (i32, &'static str, JsonValue) {
return (self.0, self.1, self.2);
}
pub fn success(data: JsonValue, msg: &str) -> Self {
let data = object! {code: 0,data:data,msg: msg.to_string()};
Self(200, "json", data)
}
pub fn fail(msg: &str) -> Self {
let data = object! {
code: -1,
msg: msg.to_string()
};
Self(200, "json", data)
}
pub fn login(msg: &str) -> Self {
let data = object! {code: 1000,msg: msg.to_string(),};
Self(200, "json", data)
}
pub fn download(filename: &str) -> Self {
Self(200, "download", filename.into())
}
pub fn redirect(url: &str) -> Self {
Self(301, "url", url.into())
}
}
pub struct ApiModel {}
impl ApiModel {
pub fn run(args_new: Vec<String>) -> (bool, String, String, String, String, JsonValue) {
let mut args: Vec<String> = env::args().collect();
if args_new.len() > 0 {
args = args_new.clone();
}
if args.len() <= 1 {
return (false, "参数错误".to_string(), "".to_string(), "".to_string(), "".to_string(), object! {});
}
let mode = args[1].as_str();
let api = args[2].as_str();
if api.is_empty() {
return (false, "无API参数".to_string(), "".to_string(), "".to_string(), "".to_string(), object! {});
}
let api: Vec<&str> = api.split(".").collect();
if api.len() != 3 {
return (false, "API参数错误".to_string(), "".to_string(), "".to_string(), "".to_string(), object! {});
}
let data = args[3].as_str();
if data.is_empty() {
return (false, "请求参数无效".to_string(), "".to_string(), "".to_string(), "".to_string(), object! {});
}
let data = json::parse(data).unwrap();
return (true, mode.to_string(), api[0].to_string(), api[1].to_string(), api[2].to_string(), data);
}
pub fn fail(msg: &str) -> JsonValue {
let data = object! {code:-1,msg:msg};
return data;
}
}