//! 本库为Rust Tiny Orm的辅助宏 extern crate proc_macro; use proc_macro::TokenStream; use quote::quote; use syn::{parse_macro_input, DeriveInput}; /// 为结构体自动实现TinyOrmDbModel数据库模型 /// /// # 用法 /// /// #[derive(TinyOrm)] /// /// 本宏定义了如下属性,用于设置数据映射接口和参数 /// /// # 应用于结构体的属性 /// /// 1. #[orm_table_name = "core_user"] /// /// 可选属性,可设置多个。 /// /// 自定义数据库表名称。 /// 未设置则表名称为结构名称的蛇形命名, /// 例如:TestUser转换为test_user。 /// /// 2. #[orm_table_name_pref = "core"] /// /// 可选属性,可设置多个。 /// /// 为自动生成的数据库表名称增加前缀。 /// 例如:TestUser转换为core_test_user. /// /// 3. #[orm_query] /// /// 可选属性,可设置多个。 /// /// 生成Self::orm_query_with开头的获取多条记录的查询方法。 /// /// 有三个子属性: /// /// * name /// * 生成的函数名称后缀 /// * 如果name设置为tel,实际生成的方法名称为:orm_query_with_tel /// * sql_where /// * 数据库select语句的where部分 /// * 可选,未设置则为获取所有记录 /// * args /// * 生成方法的参数部分定义 /// * 参数数量应与sql_where定义的?参数数量一致 /// * 与实际函数定义的写法一致,如定义2个参数,格式为:arg1:type1,arg2:type2 /// * 实际生成的函数,会自动添加pool: &TinyOrmDbPool /// * doc /// * 生成方法的文档说明 /// /// 例如: /// /// ```ignore /// #[orm_query( /// name = "name_and_tel", /// sql_where = "user.name = ? and mobile_phone = ?", /// args = "name:&str,mobile_phone:&str", /// doc = "根据姓名和手机号查询用户", /// )] /// 自动生成如下函数: /// /// 根据姓名和手机号查询用户 /// pub async fn orm_query_with_name_and_tel(pool: &TinyOrmDbPool, name:&str,mobile_phone:&str) ->AnyhowResult>{...} /// ``` /// /// 4. #[orm_update] /// /// 可选属性,可设置多个。 /// /// 生成Self::orm_update_with开头的将更新记录方法。 /// /// 有三个子属性: /// /// * name /// * 生成的函数名称后缀 /// * 如果name设置为tel,实际生成的方法名称为:orm_update_with_tel /// * sql_set /// * 数据库update语句的set部分 /// * sql_where /// * 数据库update语句的where部分 /// * 可选,未设置则为更新所有记录 /// * args /// * 生成方法的参数部分定义 /// * 与实际函数定义的写法一致,如定义2个参数,格式为:arg1:type1,arg2:type2 /// * 参数数量应与sql_set+sql_where定义的?参数数量一致 /// * 实际生成的函数,会自动添加pool: &TinyOrmDbPool /// * doc /// * 生成方法的文档说明 /// /// 例如: /// /// ```ignore /// #[orm_update( /// name = "name_and_tel", /// sql_set = "user.name = ? ,user.mobile_phone = ?", /// sql_where = "id = ?", /// args = "id:u32,name:&str,mobile_phone:&str", /// doc = "根据id更新姓名和手机号", /// )] /// 自动生成如下更新函数: /// /// 根据id更新姓名和手机号 /// pub async orm_update_with_name_and_tel(pool: &TinyOrmDbPool, name: &str,mobile_phone: &str) -> AnyhowResult<()>{...} /// ``` /// /// 5. #[orm_delete] /// /// 可选属性,可设置多个。 /// /// 生成Self::orm_delete_with开头的删除记录方法。 /// /// /// 有三个子属性: /// /// * name /// * 生成的函数名称后缀 /// * 如果name设置为tel,实际生成的方法名称为:orm_delete_with_tel /// * sql_where /// * 数据库delete语句的where部分 /// * 可选,未设置则为删除所有记录 /// * args /// * 生成方法的参数部分定义 /// * 参数数量应与sql_where定义的?参数数量一致 /// * 与实际函数定义的写法一致,如定义2个参数,格式为:arg1:type1,arg2:type2 /// * 实际生成的函数,会自动添加pool: &TinyOrmDbPool /// * doc /// * 生成方法的文档说明 /// /// 例如: /// /// ```ignore /// #[orm_delete( /// name = "name_and_tel", /// sql_where = "user.name = ? and mobile_phone = ?" /// args = "name:&str,mobile_phone:&str", /// doc = "根据姓名和手机号删除用户" /// )] /// 自动生成如下函数: /// /// 根据姓名和手机号查询用户 /// pub async fn orm_delete_with_name_and_tel(pool: &TinyOrmDbPool, name:&str,mobile_phone:&str) ->AnyhowResult<()>{...} /// ``` /// /// 6. #[orm_exist] /// /// 可选属性,可设置多个。 /// /// 生成Self::orm_exist_with开头的删除记录方法。 /// /// /// 有三个子属性: /// /// * name /// * 生成的函数名称后缀 /// * 如果name设置为tel,实际生成的方法名称为:orm_exist_with_tel /// * sql_where /// * 数据库select语句的where部分 /// * 实际生成的sql为:select count(1) from table_name where sql_where /// * 可选,未设置则为所有记录 /// * args /// * 生成方法的参数部分定义 /// * 参数数量应与sql_where定义的?参数数量一致 /// * 与实际函数定义的写法一致,如定义2个参数,格式为:arg1:type1,arg2:type2 /// * 实际生成的函数,会自动添加pool: &TinyOrmDbPool /// * doc /// * 生成方法的文档说明 /// /// 例如: /// /// ```ignore /// #[orm_exist( /// name = "id", /// sql_where = "id = ?" /// args = "id:u32", /// doc = "根据id查询记录是否存在" /// )] /// 自动生成如下函数: /// /// 根据id查询记录是否存在 /// pub async fn orm_exist_with_name_and_tel(pool: &TinyOrmDbPool, id: u32) ->AnyhowResult /// ``` /// /// # 应用于结构体字段的方法 /// /// 1. #[orm_pk(name="id", auto="true")] /// /// 至少需要设置1个,可设置多个。 /// /// 设置当前字段为主键字段 /// /// 有两个子属性: /// /// * name /// * 设置对应数据库表中的字段名称 /// * 可选属性,如果未设置,数据库中的字段名称和结构体字段名称一致 /// * auto /// * 设置为true,则标识这是个自动增长的主键,在插入时不设置值 /// * 可选属性 /// /// 该属性会自动生成如下方法: /// /// 多个主键会自动汇聚为一个方法,并将主键设置为对应的函数方法的参数 /// /// * Self::orm_get_with_pk /// * 通过主键获取单条记录 /// * 如果设置了多个主键,会将按照字段定义顺序,合并到该方法的参数中 /// * 函数定义 /// * pub async fn orm_get_with_pk(pool: &TinyOrmDbPool,主键字段1:字段类型1,主键字段2:字段类型2,...) -> AnyhowResult /// * 结构体字段类型自动转换 /// * Option 自动转换为 T /// * String 自动转换为 &str /// * Self::orm_delete_with_pk /// * 通过主键删除记录 /// * 如果设置了多个主键,会将按照字段定义顺序,合并到该方法的参数中 /// * 函数定义 /// * pub async fn orm_delete_with_pk(pool: &TinyOrmDbPool,主键字段1:字段类型1,主键字段2:字段类型2,...) -> AnyhowResult<()> /// * 结构体字段类型自动转换 /// * Option 自动转换为 T /// * String 自动转换为 &str /// * self::orm_delete /// * 通过主键删除当前实例在数据库记录 /// * 如果设置了多个主键,会将按照字段定义顺序,合并到该方法的参数中 /// * 函数定义 /// * pub async fn orm_delete(&self,pool: &TinyOrmDbPool) -> AnyhowResult /// * Self::orm_exist_with_pk /// * 通过主键查询记录是否存在 /// * 如果设置了多个主键,会将按照字段定义顺序,合并到该方法的参数中 /// * 函数定义 /// * pub async fn orm_exists_with_pk(pool: &TinyOrmDbPool,主键字段1:字段类型1,主键字段2:字段类型2,...) -> AnyhowResult /// * 结构体字段类型自动转换 /// * Option 自动转换为 T /// * String 自动转换为 &str /// * Self::orm_exist /// * 通过主键查询当前实例在数据库记录是否存在 /// * 如果设置了多个主键,会将按照字段定义顺序,合并到该方法的参数中 /// * 函数定义 /// * pub async fn orm_exists_with_pk(&self,pool: &TinyOrmDbPool) -> AnyhowResult /// /// # 示例 /// /// ```ignore /// struct TestUser { /// #[orm_pk(name="user_id",auto="true")] /// id:u32, /// #[orm_pk] /// tel:String, /// } /// /// //以上会自动生成如下方法 /// pub async fn orm_get_with_pk(pool: &TinyOrmDbPool,id:u32,tel:&str) -> AnyhowResult{...} /// pub async fn orm_delete_with_pk(pool: &TinyOrmDbPool,id:u32,tel:&str) -> AnyhowResult<()>{...} /// pub async fn orm_exist_with_pk(pool: &TinyOrmDbPool,id:u32,tel:&str) -> AnyhowResult{...} /// pub async fn orm_delete(self,pool: &TinyOrmDbPool) -> AnyhowResult{...} /// pub async fn orm_exist(self,pool: &TinyOrmDbPool) -> AnyhowResult{...} /// /// ``` /// /// 2. #[orm_join] /// /// 可选属性,可设置多个。 /// /// 设置对应字段为关联其他表的join字段 /// /// 有六个子属性: /// /// * name /// * 设置对应数据库表中的字段名称 /// * 可选属性,如果未设置,数据库中的字段名称和结构体字段名称一致 /// * select_field /// * 从join表select数据库字段的对应sql语句 /// * 该语句会与结构体自身对应数据库表的字段合并为select的选择字段清单 /// * join /// * 连接表的join语句 /// * link_id /// * 字段的rust类型的对应连接字段 /// * 例如,如某结构体User /// * 其中某一字段定义为:user_type:UserType,设置link_id="id" /// * 写入User表记录时,会自动读取self.user_type.id为对应数据库字段的值 /// * link_id_type /// * 连接字段的rust类型 /// * 如以上UserType::id为u32类型,则link_id_type="u32" /// * skip_method /// * 是否不自动生成相关join方法 /// * true,则不自动生成 /// /// 如为设置skip_method="true",该属性会自动生成如下方法: /// /// * Self::orm_query_join_with_字段名称 /// * 使用join字段查询记录 /// * 函数定义 /// * pub async fn orm_query_join_with_字段名称(pool: &TinyOrmDbPool,字段名称:字段类型) -> AnyhowResult> /// * 结构体字段类型自动转换 /// * Option 自动转换为 T /// * String 自动转换为 &str /// * Self::orm_query_join_with_字段名称_{link_id名称} /// * 使用join字段的link_id查询记录 /// * 函数定义 /// * pub async fn orm_query_join_with_字段名称_{link_id名称}(pool: &TinyOrmDbPool,link_id名称:link_id_type名称) -> AnyhowResult> /// * Self::orm_delete_join_with_字段名称 /// * 使用join字段删除记录 /// * 函数定义 /// * pub async fn orm_delete_join_with_字段名称(pool: &TinyOrmDbPool,字段名称:字段类型) -> AnyhowResult<()> /// * 结构体字段类型自动转换 /// * Option 自动转换为 T /// * String 自动转换为 &str /// * Self::orm_delete_join_with_字段名称_{link_id名称} /// * 使用join字段的link_id删除记录 /// * 函数定义 /// * pub async fn orm_delete_join_with_字段名称_{link_id名称}(pool: &TinyOrmDbPool,link_id名称:link_id_type名称) -> AnyhowResult<()> /// * self::orm_update_join_with_字段名称 /// * 使用当前实例的对应join字段.link_id保存到数据库中对应字段中 /// * 函数定义 /// * pub async fn orm_update_join_with_字段名称(&self,pool: &TinyOrmDbPool) -> AnyhowResult<()> /// /// # 示例 /// /// ``` /// struct User { /// #[orm_join( /// name="user_type_id", /// select_field="user_type.name as user_type_name, user_type.template", /// join="JOIN user_type ON user_type.id = user_type_id", /// link_id="id", /// link_id_type="u32", /// )] /// user_type:UserType /// } /// // 会自动生成如下方法 /// pub async fn orm_query_join_with_user_type(pool: &TinyOrmDbPool,user_type:UserType) -> AnyhowResult>{...} /// pub async fn orm_query_join_with_user_type_id(pool: &TinyOrmDbPool,query_value:u32) -> AnyhowResult>{...} /// pub async fn orm_delete_join_with_user_type(pool: &TinyOrmDbPool,user_type:UserType) -> AnyhowResult<()>{...} /// pub async fn orm_delete_join_with_user_type_id(pool: &TinyOrmDbPool,query_value:u32) -> AnyhowResult<()>{...} /// pub async fn orm_update_join_with_user_type(&self,pool: &TinyOrmDbPool) -> AnyhowResult<()>{...} /// ``` /// /// 3. #[orm_field(name = "custom_table_field_name")] /// /// 可选属性,可设置多个。 /// /// 设置对应数据库表的字段名称 /// /// 有1个子属性: /// /// * name /// * 设置对应数据库表中的字段名称 /// * 可选属性,如果未设置,数据库中的字段名称和结构体字段名称一致 /// /// 4. #[orm_ignore] /// /// 可选属性,可设置多个。 /// /// 默认情况下,结构体的每个字段均对应一个数据库表字段。 /// 设置该属性后,可忽略对应字段,不对应数据库表的字段名称 /// /// 5. #[orm_update] /// /// 生成更新数据库中的对应字段值的方法,方法定义如下: /// pub async fn orm_update_字段名称(&self,pool: &TinyOrmDbPool) ->AnyhowResult<()> /// /// 6. #[orm_query] /// /// 生成使用该字段查询数据库记录的方法,方法定义如下: /// * pub async fn orm_query_with_字段名称(pool: &TinyOrmDbPool,query_value:字段类型) -> AnyhowResult> /// * 结构体字段类型自动转换 /// * Option 自动转换为 T /// * String 自动转换为 &str /// /// 7. #[orm_get] /// /// 生成使用该字段获取单条数据库记录的方法,方法定义如下: /// * pub async fn orm_get_with_字段名称(pool: &TinyOrmDbPool,query_value:字段类型) -> AnyhowResult /// * 结构体字段类型自动转换 /// * Option 自动转换为 T /// * String 自动转换为 &str /// /// # 其他自动生成方法 /// /// 1. self.orm_insert方法 /// /// 将当前结构体实例数据插入到数据库表中,用于新增记录的保存 /// /// 方法定义如下: /// pub async fn orm_insert(&mut self, pool: &TinyOrmDbPool) -> AnyhowResult<()> /// /// 2. self.orm_update_all方法 /// /// 将当前结构体实例数据更新到数据库表中,用于变更记录的保存 /// /// 方法定义如下: /// pub async fn orm_update_all(&self,pool: &TinyOrmDbPool) -> AnyhowResult<()> #[proc_macro_derive( TinyOrm, attributes( orm_table_name, orm_table_name_pref, orm_query,// 定义查询方法 orm_pk,// 标识主键,定义组键方法 orm_delete,// 定义删除方法 orm_update,// 定义update方法 orm_exist,// 定义exist方法 orm_field,// 数据库字段重命名 orm_join,// join表定义 orm_ignore,// 忽略字段,不对应数据库表字段 orm_get,// 获取单条记录 ) )] pub fn tiny_orm_derive(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); trait_tool::impl_tiny_orm(&ast) } /// 自动实现TinyOrmQuery trait宏 /// /// 自动实现相关数据库的插入、更新、查询、删除等接口 /// /// 用法如下: /// #[derive(TinyOrmQuery)] #[proc_macro_derive(TinyOrmQuery)] pub fn db_query_trait_macro_derive(input: TokenStream) -> TokenStream { let ast = syn::parse(input).unwrap(); impl_db_query_trait_macro(&ast) } /// 为请求报文自动实现DbQuery fn impl_db_query_trait_macro(ast: &syn::DeriveInput) -> TokenStream { let name = &ast.ident; let gen = quote! { #[async_trait] impl TinyOrmDbQuery for #name {} }; gen.into() } mod trait_tool;