use proc_macro::TokenStream;
use quote::quote;
use syn::MetaNameValue;
mod struct_tool;
pub(super) fn impl_tiny_orm(ast: &syn::DeriveInput) -> TokenStream {
let struct_name = &ast.ident;
let table_name = get_table_name(ast);
let struct_tool::instance_method_impl::TinyOrmStructInfo {
token: struct_impl,
pk_where_sql,
select_join_sql,
select_field,
update_set_field,
} = struct_tool::impl_struct_fn(ast, &table_name);
let update_set_sql = format!("UPDATE {} SET {} ", table_name, update_set_field);
let update_sql = format!("{update_set_sql} where {pk_where_sql}");
let select_sql = format!(
"SELECT {} FROM {} {} ",
select_field,
table_name,
select_join_sql.unwrap_or_else(|| "".into())
);
let db_model_impl = quote! {
impl TinyOrmDbModel for #struct_name {
const DB_META:TinyOrmDbMeta = TinyOrmDbMeta{
table_name: #table_name,
select_sql: #select_sql,
update_sql: #update_sql,
update_set_sql: #update_set_sql,
pk_where_sql: #pk_where_sql,
};
}
};
quote! {
#db_model_impl
#struct_impl
}
.into()
}
fn get_attribute_value(attrs: &[(String, String)], key: &str) -> Option<String> {
attrs.iter().find_map(|(name, value)| {
if name == key {
Some(value.clone())
} else {
None
}
})
}
fn get_table_name(ast: &syn::DeriveInput) -> String {
let struct_name = &ast.ident;
let attrs: Vec<_> = ast
.attrs
.iter()
.filter(|attr| {
attr.path.is_ident("orm_table_name") || attr.path.is_ident("orm_table_name_pref")
})
.map(|attr| {
let name = attr.path.get_ident().unwrap().to_string();
let meta = attr.parse_meta();
let value = match meta {
Ok(meta) => {
if let syn::Meta::NameValue(MetaNameValue { lit, .. }) = meta {
match lit {
syn::Lit::Str(s) => s.value(),
_ => panic!("orm属性设置错误,输入为字符串"),
}
} else {
panic!("orm属性设置错误,输入为字符串")
}
}
Err(_) => panic!("orm属性设置错误,输入为字符串"),
};
(name, value)
})
.collect();
let table_name_pref = get_attribute_value(&attrs, "orm_table_name_pref");
get_attribute_value(&attrs, "orm_table_name").unwrap_or_else(|| {
let mut table_name = String::new();
for (i, ch) in struct_name.to_string().char_indices() {
if i > 0 && ch.is_uppercase() {
table_name.push('_');
}
table_name.push(ch.to_ascii_lowercase());
}
if let Some(table_name_pref) = table_name_pref {
format!("{table_name_pref}_{table_name}")
} else {
table_name
}
})
}