use common_uu::{string::StringExentd};
use proc_macro::TokenStream;
use quote::ToTokens;
use syn::{Data, Fields};
use crate::utils;
pub fn db_query(input: TokenStream) -> TokenStream {
let empty = quote::quote! {};
let input = syn::parse_macro_input!(input as syn::DeriveInput);
let syn::DeriveInput {
attrs,
ident: struct_name,
data,
..
} = input;
let attrs = attrs
.iter()
.filter(|v| {
v.path
.segments
.iter()
.find(|v| v.ident.to_string() == "orm_mysql")
.is_some()
})
.collect::<Vec<_>>();
let mut table_name = String::new();
if let Some(attr) = attrs.get(0) {
let attr = attr.tokens.to_string();
let attr = attr.trim().trim_start_matches('(').trim_end_matches(')');
let attrs = attr.split_arr(",");
for ele in attrs {
let (k, v) = ele.split_two("=");
let k = k.trim().trim_end_matches("\"").trim_end_matches("\"");
let v = v.trim().trim_end_matches("\"").trim_end_matches("\"");
if k == "table_name" {
table_name = v.to_string();
} else {
panic!("supper attr: {}", v);
}
}
}
if table_name.is_empty() {
let name = struct_name.to_string();
table_name = utils::to_snake_name(&name);
}
let fields = match data {
Data::Struct(d) => d.fields,
_ => return empty.into(),
};
let fields = match fields {
Fields::Named(v) => v,
_ => return empty.into(),
};
let mut get_selfs = vec![];
let mut tys = vec![];
let mut table_fields_ident = vec![];
let mut fields_ident_init = vec![];
let mut id_field_str = String::from("id");
let mut id_field_ident = syn::Ident::new_raw("id", proc_macro2::Span::call_site());
for ele in fields.named {
let ident = ele.ident.unwrap();
let mut table_field_name = ident.to_string();
let ty = ele.ty.to_token_stream();
let attrs = ele.attrs.iter().map(|x| x.parse_meta().unwrap()).collect::<Vec<_>>();
for ele in attrs {
if let syn::Meta::List(path) = &ele{
let seg = path.path.segments.clone().into_token_stream().to_string();
let id = path.nested.clone().into_token_stream().to_string();
if seg == "orm_mysql" && id.trim() == "id"{
id_field_str = table_field_name.clone();
id_field_ident = ident.clone();
}
}
}
let attrs = ele.attrs.iter().find(|v| {
let path = v.path.to_token_stream().to_string();
let serde_s = v.tokens.to_token_stream().to_string();
path.contains("serde") && (serde_s.contains("rename") || serde_s.contains("alias"))
});
fields_ident_init.push(quote::format_ident!("{}", table_field_name));
if let Some(v) = attrs {
let r = v.tokens.to_string().split_arr(r##"""##);
let r = r[(r.len() - 2)..(r.len() - 1)].to_vec().join("");
table_field_name = r;
}
get_selfs.push(quote::quote!(#table_field_name));
table_fields_ident.push(table_field_name);
tys.push(ty);
}
let mut query_quest = vec![];
for _ in &table_fields_ident {
query_quest.push("?");
}
let query_quest = query_quest.join(",");
let table_fields_str = table_fields_ident.join(",");
let table_fields_update_str = table_fields_ident.iter().map(|x| format!("{x}=?,")).collect::<Vec<_>>().join("").trim_end_matches(",").to_string();
let is_debug_log = cfg!(feature = "debug_log");
let code = quote::quote! {
use orm_mysql::mysql_async::prelude::*;
use orm_mysql::mysql::con_value::*;
impl From<#struct_name> for orm_mysql::mysql_async::Params{
fn from(#struct_name{ #(#fields_ident_init),* }: #struct_name) -> Self{
orm_mysql::mysql_async::Params::Positional(vec![#(#fields_ident_init .to_value()),*])
}
}
impl orm_mysql::mysql_async::prelude::FromRow for #struct_name {
fn from_row_opt(row: orm_mysql::mysql_async::Row) -> Result<Self, orm_mysql::mysql_async::FromRowError>
where Self: Sized,
{
let err = orm_mysql::mysql_async::FromRowError(row.clone());
Ok(#struct_name {
#(#fields_ident_init : row[#table_fields_ident].conv().map_err(|_|err.clone())? ),*
})
}
}
impl orm_mysql::mysql::OrmMySqlTrait for #struct_name {
async fn query<C>(
comm: &mut C,
where_sql: &str,
limit: Option<usize>,
) -> common_uu::IResult<Vec<Self>>
where
Self: Sized,
C: Queryable + Send + Sync,
{
let where_sql = where_sql.trim_start_matches("where");
let where_sql = where_sql.trim_start_matches("WHERE");
let where_sql = format!("where {}", where_sql);
let table_name_var = #table_name;
let mut sql = format!("select {select_sql} from {table_name_var} {where_sql}",
select_sql = #table_fields_str,
table_name_var = table_name_var,
where_sql = where_sql,
);
if let Some(v) = limit{
sql.push_str(&format!(" limit {}", v));
}
let sql = sql.as_str();
if #is_debug_log {
orm_mysql::log::debug!("query sql: {}", sql);
}
let r = comm.query(sql).await?;
Ok(r)
}
async fn query_first<C>(
comm: &mut C,
where_sql: &str,
) -> common_uu::IResult<Option<Self>>
where
Self: Sized,
C: Queryable + Send + Sync,
{
let where_sql = where_sql.trim_start_matches("where");
let where_sql = where_sql.trim_start_matches("WHERE");
let where_sql = format!("where {}", where_sql);
let table_name_var = #table_name;
let mut sql = format!("select {select_sql} from {table_name_var} {where_sql} limit 1",
select_sql = #table_fields_str,
table_name_var = table_name_var,
where_sql = where_sql,
);
if #is_debug_log {
orm_mysql::log::debug!("query_first sql: {}", sql);
}
let r = comm.query_first(sql).await?;
Ok(r)
}
fn insert_sql() -> String {
let sql = format!("insert into {table_name_var} ({table_fields})values({query_quest})",
table_name_var = #table_name,
table_fields = #table_fields_str,
query_quest = #query_quest,
);
if #is_debug_log {
orm_mysql::log::debug!("insert sql: {sql}");
}
sql
}
async fn insert<C>(self, conn: &mut C) -> common_uu::IResult<i64>
where
Self: Sized,
C: Queryable + Send + Sync
{
let sql = Self::insert_sql();
let r: Option<(i64, )> = conn.exec_first(sql, self).await?;
let r = r.map(|v|v.0).unwrap_or_default();
Ok(r)
}
async fn update<C>(self, conn: &mut C) -> common_uu::IResult<i64>
where
Self: Sized,
C: Queryable + Send + Sync
{
let sql = format!("update {table_name_var} set {table_fields} where {where_var}={val}",
table_name_var = #table_name,
table_fields = #table_fields_update_str,
where_var = #id_field_str,
val = serde_json::json!(self.#id_field_ident).to_string()
);
if #is_debug_log {
orm_mysql::log::debug!("update sql: {sql}");
}
let r: Option<(i64, )> = conn.exec_first(sql, self).await?;
let r = r.map(|v|v.0).unwrap_or_default();
Ok(r)
}
async fn delete<C>(&self, conn: &mut C) -> common_uu::IResult<i64>
where
Self: Sized,
C: Queryable + Send + Sync
{
let sql = format!("delete from {table_name_var} where {where_var}=?",
table_name_var = #table_name,
where_var = #id_field_str,
);
if #is_debug_log {
orm_mysql::log::debug!("delete sql: {sql}");
}
let r: Option<(i64, )> = conn.exec_first(sql, (&self.#id_field_ident,)).await?;
let r = r.map(|v|v.0).unwrap_or_default();
Ok(r)
}
async fn delete_where<C>(conn: &mut C, where_sql: &str) -> common_uu::IResult<i64>
where
Self: Sized,
C: Queryable + Send + Sync
{
let where_sql = where_sql.trim_start_matches("where");
let where_sql = where_sql.trim_start_matches("WHERE");
let where_sql = format!("where {}", where_sql);
let sql = format!("delete from {table_name_var} {val}",
table_name_var = #table_name,
val = where_sql
);
if #is_debug_log {
orm_mysql::log::debug!("delete sql: {sql}");
}
let r: Option<(String, i64)> = conn.exec_first(sql, ()).await?;
let r = r.map(|v|v.1).unwrap_or_default();
Ok(r)
}
fn delete_sql() -> String
where
Self: Sized
{
let sql = format!("delete from {table_name_var} where {where_var}=?",
table_name_var = #table_name,
where_var = #id_field_str,
);
sql
}
fn where_id(&self) -> impl Into<orm_mysql::mysql_async::Value>{
&self.#id_field_ident
}
}
};
code.into()
}