1
1
use chrono:: { NaiveDateTime , Utc } ;
2
- use log:: LevelFilter ;
2
+ use futures_util:: TryStreamExt ;
3
+ use log:: { info, LevelFilter } ;
3
4
use ormx:: { Delete , Insert , Table } ;
4
5
use sqlx:: PgPool ;
5
6
6
7
mod query2;
7
8
8
- #[ tokio:: main]
9
- async fn main ( ) -> anyhow:: Result < ( ) > {
10
- dotenv:: dotenv ( ) . ok ( ) ;
11
- env_logger:: builder ( )
12
- . filter_level ( LevelFilter :: Debug )
13
- . init ( ) ;
14
-
15
- let db = PgPool :: connect ( & dotenv:: var ( "DATABASE_URL" ) ?) . await ?;
16
-
17
- log:: info!( "insert a few new rows into the database" ) ;
18
- let mut new = InsertUser {
19
- user_id : 1 ,
20
- first_name : "Moritz" . to_owned ( ) ,
21
- last_name : "Bischof" . to_owned ( ) ,
22
- email : "[email protected] " . to_owned ( ) ,
23
- disabled : None ,
24
- role : Role :: User ,
25
- ty : Some ( AccountType :: Normal ) ,
26
- }
27
- . insert ( & db)
28
- . await ?;
29
- InsertUser {
30
- user_id : 2 ,
31
- first_name : "Dylan" . to_owned ( ) ,
32
- last_name : "Thomas" . to_owned ( ) ,
33
- email : "[email protected] " . to_owned ( ) ,
34
- disabled : Some ( "email not verified" . to_owned ( ) ) ,
35
- role : Role :: Admin ,
36
- ty : None ,
37
- }
38
- . insert ( & db)
39
- . await ?;
40
-
41
- log:: info!( "update a single field" ) ;
42
- new. set_last_login ( & db, Some ( Utc :: now ( ) . naive_utc ( ) ) )
43
- . await ?;
44
-
45
- log:: info!( "update all fields at once" ) ;
46
- new. email = "asdf" . to_owned ( ) ;
47
- new. update ( & db) . await ?;
48
-
49
- log:: info!( "apply a patch to the user" ) ;
50
- new. patch (
51
- & db,
52
- UpdateUser {
53
- first_name : "NewFirstName" . to_owned ( ) ,
54
- last_name : "NewLastName" . to_owned ( ) ,
55
- disabled : Some ( "Reason" . to_owned ( ) ) ,
56
- role : Role :: Admin ,
57
- } ,
58
- )
59
- . await ?;
60
-
61
- log:: info!( "reload the user, in case it has been modified" ) ;
62
- new. email . clear ( ) ;
63
- new. reload ( & db) . await ?;
64
-
65
- log:: info!( "use the improved query macro for searching users" ) ;
66
- let search_result = query2:: query_users ( & db, Some ( "NewFirstName" ) , None ) . await ?;
67
- log:: info!( "search result: {:?}" , search_result) ;
68
-
69
- log:: info!( "load all users in the order specified by the 'order_by' attribute" ) ;
70
- let all = User :: all_paginated ( & db, 0 , 100 ) . await ?;
71
- log:: info!( "all users: {all:?}" ) ;
72
-
73
- log:: info!( "delete the user from the database" ) ;
74
- new. delete ( & db) . await ?;
75
-
76
- Ok ( ( ) )
77
- }
78
-
79
9
#[ derive( Debug , ormx:: Table ) ]
80
10
#[ ormx( table = "users" , id = user_id, insertable, deletable, order_by = "email ASC" ) ]
81
11
struct User {
82
- // map this field to the column "id"
83
- #[ ormx( column = "id" ) ]
84
- #[ ormx( get_one = get_by_user_id) ]
12
+ // `#[ormx(default)]` indicates that the database generates a value for us.
13
+ // ` #[ormx(get_one = ..)]` generates `User::get_by_user_id(db, id: i32) -> Result<User>` for us
14
+ #[ ormx( column = "id" , default , get_one = get_by_user_id) ] // map this field to the column "id"
85
15
user_id : i32 ,
16
+
17
+ // just some normal, 'NOT NULL' columns
86
18
first_name : String ,
87
19
last_name : String ,
88
- // generate `User::by_email(&str) -> Result<Option<Self>>`
20
+ disabled : Option < String > ,
21
+
22
+ // generates `User::by_email(&str) -> Result<Option<Self>>`
23
+ // unlike `#[ormx(get_one = .. )]`, `by_email` will return `None` instead of an error if no record is found.
89
24
#[ ormx( get_optional( & str ) ) ]
90
25
email : String ,
26
+
27
+ // custom types need to be annotated with `#[ormx(custom_type)]`
91
28
#[ ormx( custom_type) ]
92
29
role : Role ,
30
+
31
+ // they can, of course, also be nullable
93
32
#[ ormx( column = "type" , custom_type) ]
94
33
ty : Option < AccountType > ,
34
+
35
+ // the database can also provide a default value for them.
36
+ // `#[ormx(set)]` generates `User::set_group(&mut self, g: UserGroup) -> Result` for us
95
37
#[ ormx( custom_type, default , set) ]
96
38
group : UserGroup ,
97
- disabled : Option < String > ,
98
- // don't include this field into `InsertUser` since it has a default value
99
- // generate `User::set_last_login(Option<NaiveDateTime>) -> Result<()>`
39
+
40
+ // besides enums, composite/record types are also supported
41
+ #[ ormx( custom_type, default ) ]
42
+ favourite_color : Option < Color > ,
43
+
44
+ // generates `User::set_last_login(&mut self, Option<NaiveDateTime>) -> Result`
100
45
#[ ormx( default , set) ]
101
46
last_login : Option < NaiveDateTime > ,
102
47
}
@@ -112,6 +57,8 @@ struct UpdateUser {
112
57
role : Role ,
113
58
}
114
59
60
+ // these are all enums, created using `CREATE TYPE .. AS ENUM (..);`
61
+
115
62
#[ derive( Debug , Copy , Clone , sqlx:: Type ) ]
116
63
#[ sqlx( type_name = "user_role" ) ]
117
64
#[ sqlx( rename_all = "lowercase" ) ]
@@ -137,10 +84,98 @@ enum UserGroup {
137
84
Other ,
138
85
}
139
86
87
+ // PostgreSQL also supports composite/record types
88
+
89
+ #[ derive( Debug , Copy , Clone , sqlx:: Type ) ]
90
+ #[ sqlx( type_name = "color" ) ]
91
+ struct Color {
92
+ red : i32 ,
93
+ green : i32 ,
94
+ blue : i32 ,
95
+ }
96
+
140
97
#[ derive( Debug , ormx:: Table ) ]
141
98
#[ ormx( table = "test" , id = id, insertable) ]
142
99
struct Test {
143
100
id : i32 ,
144
101
#[ ormx( by_ref) ]
145
102
rows : Vec < String > ,
146
103
}
104
+
105
+ #[ tokio:: main]
106
+ async fn main ( ) -> anyhow:: Result < ( ) > {
107
+ dotenv:: dotenv ( ) . ok ( ) ;
108
+ env_logger:: builder ( ) . filter_level ( LevelFilter :: Info ) . init ( ) ;
109
+
110
+ let pool = PgPool :: connect ( & dotenv:: var ( "DATABASE_URL" ) ?) . await ?;
111
+ let mut tx = pool. begin ( ) . await ?;
112
+
113
+
114
+ info ! ( "insert a new row into the database.." ) ;
115
+ let mut new = InsertUser {
116
+ first_name : "Moritz" . to_owned ( ) ,
117
+ last_name : "Bischof" . to_owned ( ) ,
118
+ email : "[email protected] " . to_owned ( ) ,
119
+ disabled : None ,
120
+ role : Role :: User ,
121
+ ty : Some ( AccountType :: Normal ) ,
122
+ }
123
+ . insert ( & mut * tx)
124
+ . await ?;
125
+ info ! ( "after inserting a row, ormx loads the database-generated columns for us, including the ID ({})" , new. user_id) ;
126
+
127
+
128
+ info ! ( "update a single field at a time, each in its own query.." ) ;
129
+ new. set_last_login ( & mut * tx, Some ( Utc :: now ( ) . naive_utc ( ) ) )
130
+ . await ?;
131
+ new. set_group ( & mut * tx, UserGroup :: Global ) . await ?;
132
+
133
+
134
+ info ! ( "update all fields at once.." ) ;
135
+ new. email = "asdf" . to_owned ( ) ;
136
+ new. favourite_color = Some ( Color {
137
+ red : 255 ,
138
+ green : 0 ,
139
+ blue : 0 ,
140
+ } ) ;
141
+ new. update ( & mut * tx) . await ?;
142
+
143
+
144
+ info ! ( "apply a patch to the user.." ) ;
145
+ new. patch (
146
+ & mut * tx,
147
+ UpdateUser {
148
+ first_name : "NewFirstName" . to_owned ( ) ,
149
+ last_name : "NewLastName" . to_owned ( ) ,
150
+ disabled : Some ( "Reason" . to_owned ( ) ) ,
151
+ role : Role :: Admin ,
152
+ } ,
153
+ )
154
+ . await ?;
155
+
156
+
157
+ info ! ( "reload the user, in case it has been modified.." ) ;
158
+ new. email . clear ( ) ;
159
+ new. reload ( & mut * tx) . await ?;
160
+
161
+
162
+ info ! ( "use the improved query macro for searching users.." ) ;
163
+ let search_result = query2:: query_users ( & mut * tx, Some ( "NewFirstName" ) , None ) . await ?;
164
+ info ! ( "found {} matching users" , search_result. len( ) ) ;
165
+
166
+
167
+ info ! ( "load all users in the order specified by the 'order_by' attribute.." ) ;
168
+ User :: stream_all_paginated ( & mut * tx, 0 , 100 )
169
+ . try_for_each ( |u| async move {
170
+ info ! ( "- user_id = {}" , u. user_id) ;
171
+ Ok ( ( ) )
172
+ } )
173
+ . await ?;
174
+
175
+
176
+ info ! ( "delete the user from the database.." ) ;
177
+ new. delete ( & mut * tx) . await ?;
178
+
179
+
180
+ Ok ( ( ) )
181
+ }
0 commit comments