@@ -395,6 +395,73 @@ impl<T, C> PaginatedQueryWithCountSubq<T, C> {
395
395
}
396
396
}
397
397
398
+ macro_rules! seek {
399
+ (
400
+ $vis: vis enum $name: ident {
401
+ $(
402
+ $variant: ident( $( $( #[ $field_meta: meta] ) ? $ty: ty) ,* )
403
+ ) *
404
+ }
405
+ ) => {
406
+ paste:: item! {
407
+ $(
408
+ #[ derive( Debug , Default , Deserialize , Serialize , PartialEq ) ]
409
+ $vis struct $variant( $( $( #[ $field_meta] ) ? pub ( super ) $ty) ,* ) ;
410
+ ) *
411
+
412
+ #[ derive( Debug , Deserialize , Serialize , PartialEq ) ]
413
+ #[ serde( untagged) ]
414
+ $vis enum [ <$name Payload >] {
415
+ $(
416
+ $variant( $variant) ,
417
+ ) *
418
+ }
419
+
420
+ #[ derive( Debug , PartialEq ) ]
421
+ $vis enum $name {
422
+ $(
423
+ $variant,
424
+ ) *
425
+ }
426
+
427
+ $(
428
+ impl From <$variant> for [ <$name Payload >] {
429
+ fn from( value: $variant) -> Self {
430
+ [ <$name Payload >] :: $variant( value)
431
+ }
432
+ }
433
+ ) *
434
+ impl From <[ <$name Payload >] > for $name {
435
+ fn from( value: [ <$name Payload >] ) -> Self {
436
+ match value {
437
+ $(
438
+ [ <$name Payload >] :: $variant( _) => $name:: $variant,
439
+ ) *
440
+ }
441
+ }
442
+ }
443
+
444
+ use crate :: util:: errors:: AppResult ;
445
+ use crate :: controllers:: helpers:: pagination:: Page ;
446
+ impl $name {
447
+ pub fn after( & self , page: & Page ) -> AppResult <Option <[ <$name Payload >] >> {
448
+ let Page :: Seek ( ref encoded) = * page else {
449
+ return Ok ( None ) ;
450
+ } ;
451
+
452
+ Ok ( Some ( match self {
453
+ $(
454
+ $name:: $variant => encoded. decode:: <$variant>( ) ?. into( ) ,
455
+ ) *
456
+ } ) )
457
+ }
458
+ }
459
+ }
460
+ } ;
461
+ }
462
+
463
+ pub ( crate ) use seek;
464
+
398
465
#[ cfg( test) ]
399
466
mod tests {
400
467
use super :: * ;
@@ -499,6 +566,66 @@ mod tests {
499
566
) ;
500
567
}
501
568
569
+ mod seek {
570
+ use chrono:: naive:: serde:: ts_microseconds;
571
+ seek ! {
572
+ pub ( super ) enum Seek {
573
+ Id ( i32 )
574
+ New ( #[ serde( with="ts_microseconds" ) ] chrono:: NaiveDateTime , i32 )
575
+ RecentDownloads ( Option <i64 >, i32 )
576
+ }
577
+ }
578
+ }
579
+
580
+ #[ test]
581
+ fn test_seek_macro_encode_and_decode ( ) {
582
+ use chrono:: { NaiveDate , NaiveDateTime } ;
583
+ use seek:: * ;
584
+
585
+ let assert_decode_after = |seek : Seek , query : & str , expect| {
586
+ let pagination = PaginationOptions :: builder ( )
587
+ . enable_seek ( true )
588
+ . gather ( & mock ( query) )
589
+ . unwrap ( ) ;
590
+ let decoded = seek. after ( & pagination. page ) . unwrap ( ) ;
591
+ assert_eq ! ( decoded, expect) ;
592
+ } ;
593
+
594
+ let seek = Seek :: Id ;
595
+ let payload = SeekPayload :: Id ( Id ( 1234 ) ) ;
596
+ let query = format ! ( "seek={}" , encode_seek( & payload) . unwrap( ) ) ;
597
+ assert_decode_after ( seek, & query, Some ( payload) ) ;
598
+
599
+ let dt: NaiveDateTime = NaiveDate :: from_ymd_opt ( 2016 , 7 , 8 )
600
+ . unwrap ( )
601
+ . and_hms_opt ( 9 , 10 , 11 )
602
+ . unwrap ( ) ;
603
+ let seek = Seek :: New ;
604
+ let payload = SeekPayload :: New ( New ( dt, 1234 ) ) ;
605
+ let query = format ! ( "seek={}" , encode_seek( & payload) . unwrap( ) ) ;
606
+ assert_decode_after ( seek, & query, Some ( payload) ) ;
607
+
608
+ let seek = Seek :: RecentDownloads ;
609
+ let payload = SeekPayload :: RecentDownloads ( RecentDownloads ( Some ( 5678 ) , 1234 ) ) ;
610
+ let query = format ! ( "seek={}" , encode_seek( & payload) . unwrap( ) ) ;
611
+ assert_decode_after ( seek, & query, Some ( payload) ) ;
612
+
613
+ let seek = Seek :: Id ;
614
+ assert_decode_after ( seek, "" , None ) ;
615
+
616
+ let seek = Seek :: Id ;
617
+ let payload = SeekPayload :: RecentDownloads ( RecentDownloads ( Some ( 5678 ) , 1234 ) ) ;
618
+ let query = format ! ( "seek={}" , encode_seek( payload) . unwrap( ) ) ;
619
+ let pagination = PaginationOptions :: builder ( )
620
+ . enable_seek ( true )
621
+ . gather ( & mock ( & query) )
622
+ . unwrap ( ) ;
623
+ let error = seek. after ( & pagination. page ) . unwrap_err ( ) ;
624
+ assert_eq ! ( error. to_string( ) , "invalid seek parameter" ) ;
625
+ let response = error. response ( ) ;
626
+ assert_eq ! ( response. status( ) , StatusCode :: BAD_REQUEST ) ;
627
+ }
628
+
502
629
fn mock ( query : & str ) -> Request < ( ) > {
503
630
Request :: builder ( )
504
631
. method ( Method :: GET )
0 commit comments