sqlx_model/
macros.rs

1#[macro_export]
2/// 对指定结构体实现名为 $option_struct_name 的可选引用struct
3/// @param $struct_name 结构体名
4/// @param $option_struct_name 更改值临时存储的结构体名
5/// @param {$name:$type} 字段名列表:类型列表
6macro_rules! model_table_ref_define {
7    ($db_type:ty,$self_var:ident,$struct_name:ident,$option_struct_name:ident,{$($name:ident[$column_name:literal]:$type:ty),+})=>{
8        #[derive(PartialEq,Eq,Debug)]
9        pub struct $option_struct_name<'t> {
10            $(pub $name:Option<&'t $type>),*
11        }
12        impl<'t> $option_struct_name<'t> {
13            #[allow(dead_code)]
14            pub fn none_default()->Self{
15                $option_struct_name {
16                    $($name:None),*
17                }
18            }
19        }
20        impl<'t> $crate::InsertData<'t,$db_type> for $option_struct_name<'t>
21        {
22            fn columns(&$self_var) -> Vec<$crate::FieldItem> {
23                let mut vec = vec![];
24                $(
25                    if !$self_var.$name.is_none() {
26                        vec.push($crate::FieldItem::new(stringify!($name),$column_name));
27                    }
28                ) *
29                vec
30            }
31            fn sqlx_bind<'q>(&'q
32                $self_var,
33                field:&$crate::FieldItem,
34                mut res: sqlx::query::Query<'q,$db_type,<$db_type as sqlx::database::HasArguments<'q>>::Arguments>,
35            ) -> sqlx::query::Query<'q,$db_type,<$db_type as sqlx::database::HasArguments<'q>>::Arguments>{
36                $crate::model_table_value_bind_define!(value_bind $self_var, res, field, {$($name),+});
37            }
38            fn sqlx_string(&$self_var,
39                field:&$crate::FieldItem
40            ) -> Option<String>{
41                use $crate::SqlQuote;
42                match field.name.as_str() {
43                    $(
44                        stringify!($name)=> {
45                            Some($self_var.$name.map_or("".to_string(),|e|{
46                                e.sql_quote().to_string()
47                            }))
48                        }
49                    ) *
50                    _=>None
51                }
52            }
53        }
54        impl<'t> $crate::ModelInsertData<'t,$db_type,$option_struct_name<'t>> for $struct_name
55        {
56            fn insert_data(&'t $self_var) -> $option_struct_name<'t>{
57                $option_struct_name {
58                    $(
59                       $name:Some(&$self_var.$name)
60                    ),*
61                }
62            }
63        }
64        impl<'t> $crate::UpdateData<'t,$db_type> for $option_struct_name<'t>
65        {
66            fn diff_columns(&$self_var) -> Vec<$crate::FieldItem> {
67                let mut vec = vec![];
68                $(
69                    if !$self_var.$name.is_none() {
70                        vec.push($crate::FieldItem::new(stringify!($name),$column_name));
71                    }
72                ) *
73                vec
74            }
75            fn sqlx_bind<'q>(&'q
76                $self_var,
77                mut res: sqlx::query::Query<'q,$db_type,<$db_type as sqlx::database::HasArguments<'q>>::Arguments>,
78            ) -> sqlx::query::Query<'q,$db_type,<$db_type as sqlx::database::HasArguments<'q>>::Arguments>
79            {
80                $(
81                    if let Some(val) = $self_var.$name {
82                        res = res.bind(val.clone());
83                    }
84                ) *
85                res
86            }
87            fn sqlx_string(&$self_var,field:&$crate::FieldItem) -> Option<String>
88            {
89                use $crate::SqlQuote;
90                match(field.name.as_str()){
91                    $(
92                        stringify!($name)=>{
93                            if let Some(val) = $self_var.$name {
94                                return Some(val.sql_quote().to_string())
95                            }
96                        }
97                    ) *
98                    _=>{}
99                }
100                None
101            }
102        }
103        impl<'t> $crate::ModelUpdateData<'t,$db_type, $option_struct_name<'t>> for $struct_name
104        {
105            fn diff(&'t $self_var, source_opt: &Option<&Self>) -> $option_struct_name<'t> {
106
107                match source_opt {
108                    Some(source) => {
109                        $option_struct_name {$(
110                            $name: if $self_var.$name != source.$name {
111                                Some(&$self_var.$name)
112                            } else {
113                                None
114                            }
115                        ),*}
116                    }
117                    None => $option_struct_name {
118                        $(
119                           $name:Some(&$self_var.$name)
120                        ),*
121                    },
122                }
123            }
124        }
125    };
126    ($db_type:ty,$struct_name:ident,$option_struct_name:ident,{$($name:ident[$column_name:literal]:$type:ty),+$(,)?})=>{
127        $crate::model_table_ref_define!($db_type,self,$struct_name,$option_struct_name,{$($name[$column_name]:$type),+});
128    };
129}
130
131#[macro_export]
132/// 对指定结构体 ModelTableName ModelTableField
133/// @param $struct_name 结构体名
134/// @param $table_name 表名
135/// @param {$name} 字段名列表
136/// @param {$pk_name} 主键字段名列表
137macro_rules! model_table_value_bind_define {
138    (value_bind $self_var:ident,$res:expr,$val:expr,{$($name:ident),+})=>{
139            match $val.name.as_str() {
140                $(
141                    stringify!($name)=> {
142                        $res=$res.bind(&$self_var.$name);
143                    }
144                ) *
145                _=>{}
146            }
147            return $res
148    };
149    ($db_type:ty,$self_var:ident,$struct_name:ident,$table_name:expr,{$($name:ident[$column_name:literal]),+},{$($pk_name:ident[$pk_column_name:literal]),+})=>{
150        impl $crate::ModelTableName for $struct_name {
151            fn table_name() -> $crate::TableName {
152                $crate::TableName::new($table_name)
153            }
154        }
155        impl $crate::ModelTableField<$db_type> for $struct_name{
156            fn table_pk() -> $crate::TableFields {
157                $crate::TableFields::new(vec![
158                    $(
159                        $crate::FieldItem::new(stringify!($pk_name),$pk_column_name)
160                    ),*
161                ])
162            }
163            fn table_column() -> $crate::TableFields {
164                $crate::TableFields::new(vec![
165                    $(
166                        $crate::FieldItem::new(stringify!($name),$column_name)
167                    ),*
168                ])
169            }
170            fn query_sqlx_bind<'t>(
171                &'t
172                $self_var,
173                field_val: &$crate::FieldItem,
174                mut res: sqlx::query::Query<'t,$db_type,<$db_type as sqlx::database::HasArguments<'t>>::Arguments>,
175            ) -> sqlx::query::Query<'t,$db_type,<$db_type as sqlx::database::HasArguments<'t>>::Arguments>
176            {
177                $crate::model_table_value_bind_define!(value_bind $self_var, res, field_val, {$($name),+});
178            }
179            fn query_as_sqlx_bind<'t, M>(
180                &'t $self_var,
181                field_val: &$crate::FieldItem,
182                mut res:  sqlx::query::QueryAs<'t,$db_type, M,<$db_type as sqlx::database::HasArguments<'t>>::Arguments>,
183            ) -> sqlx::query::QueryAs<'t,$db_type, M,<$db_type as sqlx::database::HasArguments<'t>>::Arguments>
184            where
185                for<'r> M: sqlx::FromRow<'r, <$db_type as sqlx::Database>::Row> + Send + Unpin,
186            {
187                $crate::model_table_value_bind_define!(value_bind $self_var, res, field_val,{$($name),+});
188            }
189        }
190    };
191    ($db_type:ty,$struct_name:ident,$table_name:expr,{$($name:ident[$column_name:literal]),+},{$($pk_name:ident[$pk_column_name:literal]),+$(,)?})=>{
192        $crate::model_table_value_bind_define!($db_type,self ,$struct_name,$table_name,{$($name[$column_name]),+},{$($pk_name[$pk_column_name]),+});
193    };
194}
195
196#[test]
197fn test_model_define_bind_macro() {
198    pub struct UserModel {
199        pub id: u32,
200        pub nickname: String,
201        pub gender: u8,
202        pub headimg: Option<String>,
203        pub password_id: u32,
204    }
205    crate::model_table_value_bind_define!(sqlx::MySql,UserModel,"user",{
206        id["id"],
207        nickname["nickname"],
208        gender["gender"],
209        headimg["headimg"],
210        password_id["password_id"]
211    },{
212        id["id"]
213    });
214    crate::model_table_ref_define!(sqlx::MySql,UserModel,UserModelRef,{
215        id["id"]: u32,
216        nickname["nickname"]: String,
217        gender["gender"]: u8,
218        headimg["headimg"]: Option<String>,
219        password_id["password_id"]: u32,
220    });
221}
222
223#[macro_export]
224/// 对实现 none_default 方法的struct 用指定键值对快速创建结构 可由model_table_ref_define实现
225/// @param $struct_name 结构体名
226/// @param $key 字段名
227/// @param $val 数据
228macro_rules! model_option_set {
229    ($struct_name:ident,{$($key:ident:$val:expr),*$(,)?})=>{
230        {
231            $struct_name{
232                $(
233                    $key:Some(&$val),
234                )*
235                ..$struct_name::none_default()
236            }
237        }
238    };
239}
240
241#[macro_export]
242/// 对实现 none_default 方法的struct 通过指定变量跟关系快速创建结构 可由model_table_ref_define实现
243/// @param $struct_name 结构体名
244/// @param $var 数据来源变量
245/// @param $key 字段映射关系,例如:0=>fieldname
246macro_rules! model_option_map {
247    ($struct_name:ident,$var:expr,{$($key:ident),*$(,)?})=>{
248        {
249            $struct_name{
250                $(
251                    $key:Some(&$var.$key),
252                )*
253                ..$struct_name::none_default()
254            }
255        }
256    };
257    ($struct_name:ident,$var:expr,{$($from_key:tt=>$to_key:ident),*$(,)?})=>{
258        {
259            $struct_name{
260                $(
261                    $to_key:Some(&$var.$from_key),
262                )*
263                ..$struct_name::none_default()
264            }
265        }
266    };
267}
268
269#[test]
270fn test_model_option_macro() {
271    #[derive(Clone, Debug)]
272    #[allow(dead_code)]
273    struct UserModel {
274        id: u32,
275        nickname: String,
276        gender: u8,
277        headimg: String,
278        password_id: u32,
279    }
280    #[derive(PartialEq, Eq, Debug)]
281    struct UserModelOption<'t> {
282        id: Option<&'t u32>,
283        nickname: Option<&'t String>,
284        gender: Option<&'t u8>,
285        headimg: Option<&'t String>,
286        password_id: Option<&'t u32>,
287    }
288    impl<'t> UserModelOption<'t> {
289        pub fn none_default() -> Self {
290            UserModelOption {
291                nickname: None,
292                gender: None,
293                id: None,
294                headimg: None,
295                password_id: None,
296            }
297        }
298    }
299
300    //test
301    let tvar1 = ("option insert".to_string(), 1);
302    let tmp = crate::model_option_map!(UserModelOption,tvar1,{0=>nickname,1=>gender});
303    assert_eq!(tmp.nickname.unwrap(), &tvar1.0);
304    assert_eq!(tmp.gender.unwrap(), &tvar1.1);
305
306    //test
307    struct Tvar {
308        a: String,
309        b: u8,
310    }
311    let tvar1 = Tvar {
312        a: "option insert".to_string(),
313        b: 1,
314    };
315    let tmp = crate::model_option_map!(UserModelOption,tvar1,{a=>nickname,b=>gender});
316    assert_eq!(
317        tmp,
318        UserModelOption {
319            nickname: Some(&tvar1.a),
320            gender: Some(&tvar1.b),
321            id: None,
322            headimg: None,
323            password_id: None
324        }
325    );
326
327    //test
328    let nike_name = "option insert".to_string();
329    let gender = 1;
330    let userinsert = crate::model_option_set!(UserModelOption,{
331        nickname:nike_name,
332        gender:gender,
333    });
334    assert_eq!(
335        userinsert,
336        UserModelOption {
337            nickname: Some(&tvar1.a),
338            gender: Some(&tvar1.b),
339            id: None,
340            headimg: None,
341            password_id: None
342        }
343    );
344
345    //test
346    struct TVAR1 {
347        nickname: String,
348        gender: u8,
349    }
350    let tvar1 = TVAR1 {
351        nickname: "option insert".to_string(),
352        gender: 1,
353    };
354    let tmp = crate::model_option_map!(UserModelOption,tvar1,{nickname,gender});
355    assert_eq!(
356        tmp,
357        UserModelOption {
358            nickname: Some(&tvar1.nickname),
359            gender: Some(&tvar1.gender),
360            id: None,
361            headimg: None,
362            password_id: None
363        }
364    );
365}
366
367#[macro_export]
368/// 对状态类型的结构提供辅助方法
369/// @param $enum_name 状态枚举
370/// @param $type 状态的类型
371/// @param $item 可选值列表
372macro_rules! model_enum_status_define {
373    ($self_var:ident,$enum_name:ident,$type:ty,{$($item:expr),*$(,)?})=>{
374        impl $enum_name{
375            pub fn eq(self,eq:$type)->bool{
376                return self.to()==eq;
377            }
378            pub fn to(self)->$type{
379                return self as $type
380            }
381        }
382		impl $crate::SqlQuote<$type> for $enum_name {
383			fn sql_quote(&self) -> $type {
384				*self as $type
385			}
386		}
387        impl std::convert::TryFrom<$type> for $enum_name {
388            type Error=sqlx::Error;
389            fn try_from(value:  $type) -> Result<Self, Self::Error> {
390                $(
391                    if ($item as $type) ==value {
392                        return Ok($item);
393                    }
394                )*
395                return Err(sqlx::Error::TypeNotFound { type_name: format!("{}[{}]->{}",stringify!(i8),value,stringify!($enum_name)) })
396            }
397        }
398    };
399    ($enum_name:ident,$type:ty,{$($item:expr),*$(,)?})=>{
400        $crate::model_enum_status_define!(self ,$enum_name,$type,{$(
401            $item,
402        )*});
403    };
404    ($enum_name:ident,$type:ty)=>{
405        $crate::model_enum_status_define!(self ,$enum_name,$type,{});
406    };
407}
408
409#[test]
410fn test_model_enum_status() {
411    #[derive(PartialEq, Eq, Clone, Copy)]
412    enum UserModelStatus {
413        Statu1 = 1,
414        Statu2 = 2,
415    }
416    crate::model_enum_status_define!(UserModelStatus,u8,{
417        UserModelStatus::Statu1,
418        UserModelStatus::Statu2
419    });
420    assert!(UserModelStatus::Statu1.eq(1));
421    assert!(!UserModelStatus::Statu1.eq(2));
422    assert!(UserModelStatus::Statu2.eq(2));
423    let status: UserModelStatus = 2.try_into().unwrap();
424    assert!(status == UserModelStatus::Statu2);
425    let status: Result<UserModelStatus, _> = 3.try_into();
426    assert!(status.is_err());
427}