@@ -6,6 +6,12 @@ use crate::util::deserialize_bool;
6
6
7
7
pub type Error = serde_qs:: Error ;
8
8
9
+ #[ derive( Clone , Debug , PartialEq ) ]
10
+ pub enum CursorType {
11
+ Blob ,
12
+ Integer ,
13
+ }
14
+
9
15
/// TrailBase supports cursors in a few formats:
10
16
/// * Integers
11
17
/// * Text-encoded UUIDs ([u8; 16])
@@ -19,42 +25,25 @@ pub enum Cursor {
19
25
Integer ( i64 ) ,
20
26
}
21
27
22
- impl < ' de > serde:: de:: Deserialize < ' de > for Cursor {
23
- fn deserialize < D > ( deserializer : D ) -> Result < Self , D :: Error >
24
- where
25
- D : serde:: de:: Deserializer < ' de > ,
26
- {
27
- use serde:: de:: Error ;
28
- use serde_value:: Value ;
28
+ impl Cursor {
29
+ pub fn parse ( s : & str , cursor_type : CursorType ) -> Result < Self , Error > {
30
+ return match cursor_type {
31
+ CursorType :: Integer => {
32
+ let i = s. parse :: < i64 > ( ) . map_err ( |err| Error :: ParseInt ( err) ) ?;
33
+ Ok ( Self :: Integer ( i) )
34
+ }
35
+ CursorType :: Blob => {
36
+ if let Ok ( uuid) = uuid:: Uuid :: parse_str ( & s) {
37
+ return Ok ( Cursor :: Blob ( uuid. into ( ) ) ) ;
38
+ }
29
39
30
- static EXPECTED : & str = "integer or url-safe base64 encoded byte cursor" ;
40
+ if let Ok ( base64) = BASE64_URL_SAFE . decode ( & s) {
41
+ return Ok ( Cursor :: Blob ( base64) ) ;
42
+ }
31
43
32
- let value = Value :: deserialize ( deserializer) ?;
33
- let Value :: String ( str) = value else {
34
- return Err ( Error :: invalid_type (
35
- crate :: util:: unexpected ( & value) ,
36
- & EXPECTED ,
37
- ) ) ;
44
+ Err ( Error :: Custom ( format ! ( "Failed to parse: {s}" ) ) )
45
+ }
38
46
} ;
39
-
40
- // FIXME: This is brittle. The consumer should explicitly define what kind of cursor we expect
41
- // based on table schema.
42
- if let Ok ( integer) = str. parse :: < i64 > ( ) {
43
- return Ok ( Cursor :: Integer ( integer) ) ;
44
- }
45
-
46
- if let Ok ( uuid) = uuid:: Uuid :: parse_str ( & str) {
47
- return Ok ( Cursor :: Blob ( uuid. into ( ) ) ) ;
48
- }
49
-
50
- if let Ok ( base64) = BASE64_URL_SAFE . decode ( & str) {
51
- return Ok ( Cursor :: Blob ( base64) ) ;
52
- }
53
-
54
- return Err ( Error :: invalid_type (
55
- crate :: util:: unexpected ( & Value :: String ( str) ) ,
56
- & EXPECTED ,
57
- ) ) ;
58
47
}
59
48
}
60
49
@@ -168,7 +157,7 @@ pub struct Query {
168
157
/// Max number of elements returned per page.
169
158
pub limit : Option < usize > ,
170
159
/// Cursor to page.
171
- pub cursor : Option < Cursor > ,
160
+ pub cursor : Option < String > ,
172
161
/// Offset to page. Cursor is more efficient when available
173
162
pub offset : Option < usize > ,
174
163
@@ -191,7 +180,7 @@ impl Query {
191
180
pub fn parse ( query : & str ) -> Result < Query , Error > {
192
181
// NOTE: We rely on non-strict mode to parse `filter[col0]=a&b%filter[col1]=c`.
193
182
let qs = serde_qs:: Config :: new ( 9 , false ) ;
194
- return qs. deserialize_str :: < Query > ( query) ;
183
+ return qs. deserialize_bytes :: < Query > ( query. as_bytes ( ) ) ;
195
184
}
196
185
}
197
186
@@ -439,27 +428,33 @@ mod tests {
439
428
assert_eq ! (
440
429
qs. deserialize_str:: <Query >( "cursor=-5" ) . unwrap( ) ,
441
430
Query {
442
- cursor: Some ( Cursor :: Integer ( - 5 ) ) ,
431
+ cursor: Some ( "-5" . to_string ( ) ) ,
443
432
..Default :: default ( )
444
433
}
445
434
) ;
446
435
447
436
let uuid = uuid:: Uuid :: now_v7 ( ) ;
437
+ let r = qs
438
+ . deserialize_str :: < Query > ( & format ! ( "cursor={}" , uuid. to_string( ) ) )
439
+ . unwrap ( ) ;
448
440
assert_eq ! (
449
- qs. deserialize_str:: <Query >( & format!( "cursor={}" , uuid. to_string( ) ) )
450
- . unwrap( ) ,
441
+ r,
451
442
Query {
452
- cursor: Some ( Cursor :: Blob ( uuid. as_bytes ( ) . into ( ) ) ) ,
443
+ cursor: Some ( uuid. to_string ( ) ) ,
453
444
..Default :: default ( )
454
445
}
455
446
) ;
447
+ assert_eq ! (
448
+ Cursor :: parse( & r. cursor. unwrap( ) , CursorType :: Blob ) . unwrap( ) ,
449
+ Cursor :: Blob ( uuid. into( ) )
450
+ ) ;
456
451
457
452
let blob = BASE64_URL_SAFE . encode ( uuid. as_bytes ( ) ) ;
458
453
assert_eq ! (
459
454
qs. deserialize_str:: <Query >( & format!( "cursor={blob}" ) )
460
455
. unwrap( ) ,
461
456
Query {
462
- cursor: Some ( Cursor :: Blob ( uuid . as_bytes ( ) . into ( ) ) ) ,
457
+ cursor: Some ( blob ) ,
463
458
..Default :: default ( )
464
459
}
465
460
) ;
0 commit comments