@@ -42,12 +42,41 @@ struct ItemRow {
42
42
}
43
43
44
44
impl LocalDb {
45
- pub fn new ( pool : SqlitePool ) -> crate :: Result < Self > {
46
- Ok ( Self { pool } )
45
+ pub fn new ( pool : SqlitePool ) -> Self {
46
+ Self { pool }
47
47
}
48
48
49
- pub async fn add ( & mut self , item : & item:: Item ) -> crate :: Result < ( ) > {
49
+ pub async fn add_tag ( & mut self , tag : & item:: Tag ) -> crate :: Result < i32 > {
50
+ let res = sqlx:: query (
51
+ "INSERT INTO tags (name, tag) VALUES (?, ?) ON CONFLICT(tag) DO NOTHING RETURNING id" ,
52
+ )
53
+ . bind ( & tag. name )
54
+ . bind ( & tag. tag )
55
+ . execute ( & self . pool )
56
+ . await ?;
57
+ Ok ( res. last_insert_rowid ( ) as i32 )
58
+ }
59
+
60
+ pub async fn get_tags ( & mut self ) -> crate :: Result < Vec < item:: Tag > > {
61
+ let res: Vec < item:: Tag > = sqlx:: query_as ( "SELECT id, tag, name FROM tags" )
62
+ . fetch_all ( & self . pool )
63
+ . await ?;
64
+ Ok ( res)
65
+ }
66
+
67
+ pub async fn link_tag ( & mut self , tag : i32 , item : i32 ) -> crate :: Result < ( ) > {
50
68
sqlx:: query (
69
+ "INSERT INTO items_tags (item_id, tag_id) VALUES (?, ?) ON CONFLICT(item_id, tag_id) DO NOTHING" ,
70
+ )
71
+ . bind ( item)
72
+ . bind ( tag)
73
+ . execute ( & self . pool )
74
+ . await ?;
75
+ Ok ( ( ) )
76
+ }
77
+
78
+ pub async fn add ( & mut self , item : & item:: Item ) -> crate :: Result < i32 > {
79
+ let resutl = sqlx:: query (
51
80
"INSERT INTO items (
52
81
pocket_id,
53
82
title,
@@ -97,7 +126,16 @@ impl LocalDb {
97
126
. bind ( item. time_favorited )
98
127
. execute ( & self . pool )
99
128
. await ?;
100
- Ok ( ( ) )
129
+
130
+ let tem_id = resutl. last_insert_rowid ( ) as i32 ;
131
+
132
+ let tags = item. tags . iter ( ) . collect_vec ( ) ;
133
+ for tag in tags {
134
+ let tag_id = self . add_tag ( tag) . await ?;
135
+ self . link_tag ( tag_id, tem_id) . await ?;
136
+ }
137
+
138
+ Ok ( tem_id as i32 )
101
139
}
102
140
103
141
pub async fn get_items ( & self ) -> crate :: Result < Vec < item:: Item > > {
@@ -155,9 +193,18 @@ impl LocalDb {
155
193
156
194
#[ cfg( test) ]
157
195
mod test {
158
- use crate :: item :: ItemStatus ;
196
+ use std :: collections :: HashSet ;
159
197
160
198
use super :: * ;
199
+ use crate :: {
200
+ item:: { ItemStatus , Tag } ,
201
+ Item ,
202
+ } ;
203
+
204
+ async fn get_db ( ) -> LocalDb {
205
+ let pool = open_database ( ":memory:" ) . await . unwrap ( ) ;
206
+ LocalDb :: new ( pool)
207
+ }
161
208
162
209
#[ tokio:: test]
163
210
async fn test_open_in_memory ( ) {
@@ -167,23 +214,68 @@ mod test {
167
214
168
215
#[ tokio:: test]
169
216
async fn test_get_items ( ) {
170
- let pool = open_database ( ":memory:" ) . await . unwrap ( ) ;
171
- let db = LocalDb :: new ( pool) . unwrap ( ) ;
217
+ let db = get_db ( ) . await ;
172
218
let items = db. get_items ( ) . await . unwrap ( ) ;
173
219
assert ! ( items. is_empty( ) ) ;
174
220
}
175
221
176
222
#[ tokio:: test]
177
223
async fn test_add_item ( ) {
178
- let pool = open_database ( ":memory:" ) . await . unwrap ( ) ;
179
- let mut db = LocalDb :: new ( pool) . unwrap ( ) ;
180
- let item = Default :: default ( ) ;
224
+ let mut db = get_db ( ) . await ;
225
+
226
+ let tag = Tag {
227
+ id : 0 ,
228
+ tag : "tag" . to_string ( ) ,
229
+ name : None ,
230
+ } ;
231
+
232
+ let item = Item {
233
+ tags : HashSet :: from ( [ Tag :: default ( ) , tag] ) ,
234
+ ..Default :: default ( )
235
+ } ;
236
+
181
237
db. add ( & item) . await . unwrap ( ) ;
238
+
182
239
let items = db. get_items ( ) . await . unwrap ( ) ;
183
240
assert_eq ! ( items. len( ) , 1 ) ;
184
241
assert_eq ! ( items[ 0 ] . title, item. title) ;
185
242
assert_eq ! ( items[ 0 ] . url, item. url) ;
186
243
assert_eq ! ( items[ 0 ] . id, 1 ) ;
187
244
assert_eq ! ( items[ 0 ] . status, ItemStatus :: Unread ) ;
245
+
246
+ assert_eq ! ( items[ 0 ] . tags. len( ) , 2 ) ;
247
+ assert ! ( items[ 0 ] . tags. iter( ) . any( |t| t. tag == "tag" ) ) ;
248
+ assert ! ( items[ 0 ] . tags. iter( ) . any( |t| t. tag == "example" ) ) ;
249
+ }
250
+
251
+ #[ tokio:: test]
252
+ async fn test_add_tag ( ) {
253
+ let mut db = get_db ( ) . await ;
254
+ let result = db. add_tag ( & Default :: default ( ) ) . await ;
255
+ assert ! ( result. is_ok( ) ) ;
256
+ assert_eq ! ( result. unwrap( ) , 1 ) ;
257
+ }
258
+
259
+ #[ tokio:: test]
260
+ async fn test_add_duplicate_tag ( ) {
261
+ let mut db = get_db ( ) . await ;
262
+ db. add_tag ( & Default :: default ( ) )
263
+ . await
264
+ . expect ( "add_tag failed" ) ;
265
+ db. add_tag ( & Default :: default ( ) )
266
+ . await
267
+ . expect ( "add tag failed" ) ;
268
+
269
+ let tags = db. get_tags ( ) . await . unwrap ( ) ;
270
+ assert_eq ! ( tags. len( ) , 1 ) ;
271
+ }
272
+
273
+ #[ tokio:: test]
274
+ async fn test_link_tag ( ) {
275
+ let mut db = get_db ( ) . await ;
276
+ let tag_id = db. add_tag ( & Tag :: default ( ) ) . await . unwrap ( ) ;
277
+ let item_id = db. add ( & Default :: default ( ) ) . await . unwrap ( ) ;
278
+ let res = db. link_tag ( tag_id, item_id) . await ;
279
+ assert ! ( res. is_ok( ) ) ;
188
280
}
189
281
}
0 commit comments