#[cfg(feature = "async-std")]
use async_std::{
fs::{OpenOptions, Path},
prelude::*,
};
#[cfg(feature = "tokio")]
use tokio::fs::OpenOptions;
#[cfg(feature = "tokio")]
use tokio::io::AsyncWriteExt;
const FLAG: &'static str = "// ***************************************以下是自定义代码区域******************************************";
const FLAG2: &'static str = r#"
/*
example: [
{"skip_fields": ["updated_at", "created_at"], "filename": "table_name1"},
{"contain_fields": ["updated_at", "created_at"], "filename": "table_name2"}
]
*/
// *************************************************************************************************"#;
use crate::table::Table;
use async_trait::async_trait;
use quicli::prelude::*;
use serde::{Deserialize, Serialize};
use sql_reverse_error::result::Result;
use std::path::Path;
use tera::{Context, Tera};
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct FilterFields {
pub skip_fields: Option<Vec<String>>,
pub contain_fields: Option<Vec<String>>,
pub filename: String,
}
async fn filter_fields(table: &Table, params: Vec<FilterFields>, ) -> Result<Vec<(Table, String)>> {
let mut list = vec![];
for field in params.iter() {
if field.skip_fields.is_some() {
let data = table
.skip_fields(field.skip_fields.to_owned().unwrap())
.await;
list.push((data, field.filename.to_owned()));
} else if field.contain_fields.is_some() {
let data = table
.contain_fields(field.contain_fields.to_owned().unwrap())
.await;
list.push((data, field.filename.to_owned()));
}
}
Ok(list)
}
#[async_trait]
pub trait Render {
async fn render_rust(
template_path: &str,
template_name: &str,
suffix: &str,
output_dir: &str,
tables: &Vec<Table>,
) -> Result<()> {
create_dir(output_dir)?;
let tera = Tera::new(template_path)?;
let mut mods = vec![];
for table in tables {
mods.push(format!("pub mod {};\n", table.table_name));
let (mut struct_str, mut custom,filepath) = Self::render_table(&tera, table, template_name, suffix, output_dir, &table.table_name).await?;
if custom != "" {
let data: Vec<&str> = custom.split("*/").collect();
let data = data.get(0).unwrap_or(&"").to_string();
let data = data.replace("/*", "");
let data = data.trim();
let params: Vec<FilterFields> = serde_json::from_str(&data).unwrap_or(vec![]);
let filters = filter_fields(table, params).await?;
for filter in filters.iter() {
mods.push(format!("pub mod {};\n", filter.1));
let (mut struct_str, custom,filepath) = Self::render_table(&tera, &filter.0, template_name, suffix, output_dir, &filter.1).await?;
struct_str = struct_str + "\n" + FLAG + custom.as_str();
async_ok!(Self::write_to_file(&filepath, &struct_str))?;
}
}
if custom.trim() == "" {
custom = FLAG2.to_owned();
}
struct_str = struct_str + "\n" + FLAG + custom.as_str();
async_ok!(Self::write_to_file(&filepath, &struct_str))?;
}
if suffix == "rs" {
let mod_path = format!("{}/mod.{}", output_dir, suffix);
async_ok!(Self::append_to_file(mods, &mod_path))?;
}
Ok(())
}
async fn render_table(
tera: &Tera,
table: &Table,
template_name: &str,
suffix: &str,
output_dir: &str,
filename: &str
) -> Result<(String, String, String)> {
let mut context = Context::new();
context.insert("template", table); context.insert("table", table);
let struct_str = tera.render(template_name, &context)?;
let filepath = format!("{}/{}.{}", output_dir, filename, suffix);
let content = read_file(&filepath).unwrap_or_default();
let vv: Vec<&str> = content.split(FLAG).collect();
let custom = vv.get(1).unwrap_or(&"").to_string();
Ok((struct_str, custom, filepath))
}
async fn write_to_file(filepath: &str, content: &str) -> Result<()> {
let filepath = Path::new(&filepath);
let s = write_to_file(filepath, content)?;
Ok(s)
}
async fn append_to_file(mods: Vec<String>, filepath: &str) -> Result<()> {
let file_content = read_file(filepath).unwrap_or_default();
for v in mods.iter() {
if !file_content.contains(v) {
let mut file =
async_ok!(OpenOptions::new().append(true).create(true).open(filepath))?;
async_ok!(file.write(v.as_bytes()))?;
};
}
Ok(())
}
}