Skip to content

Commit 9f57dd4

Browse files
committed
controllers/helpers/pagination: Add named fields struct support for seek!
1 parent 74892ed commit 9f57dd4

File tree

1 file changed

+121
-6
lines changed

1 file changed

+121
-6
lines changed

src/controllers/helpers/pagination.rs

Lines changed: 121 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -401,20 +401,56 @@ impl<T, C> PaginatedQueryWithCountSubq<T, C> {
401401
}
402402
}
403403

404+
#[allow(unused_macro_rules)]
404405
macro_rules! seek {
406+
// Tuple struct
407+
(@variant_struct $vis:vis $variant:ident($($(#[$field_meta:meta])? $ty:ty),*)) => {
408+
#[derive(Debug, Default, Deserialize, Serialize, PartialEq)]
409+
$vis struct $variant($($(#[$field_meta])? pub(super) $ty),*);
410+
};
411+
// Field struct
412+
(@variant_struct $vis:vis $variant:ident {
413+
$($(#[$field_meta:meta])? $field:ident: $ty:ty),*
414+
}) => {
415+
paste::item! {
416+
#[derive(Debug, Default, Deserialize, PartialEq)]
417+
#[serde(from = $variant "Helper")]
418+
$vis struct $variant {
419+
$($(#[$field_meta])? pub(super) $field: $ty),*
420+
}
421+
422+
#[derive(Debug, Default, Deserialize, Serialize, PartialEq)]
423+
struct [<$variant Helper>]($($(#[$field_meta])? pub(super) $ty),*);
424+
425+
impl From<[<$variant Helper>]> for $variant {
426+
fn from(helper: [<$variant Helper>]) -> Self {
427+
let [<$variant Helper>]($($field,)*) = helper;
428+
Self { $($field,)* }
429+
}
430+
}
431+
432+
impl serde::Serialize for $variant {
433+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
434+
where
435+
S: serde::Serializer,
436+
{
437+
let helper = [<$variant Helper>]($(self.$field,)*);
438+
serde::Serialize::serialize(&helper, serializer)
439+
}
440+
}
441+
}
442+
};
405443
(
406444
$vis:vis enum $name:ident {
407445
$(
408-
$variant:ident($($(#[$field_meta:meta])? $ty:ty),*)
446+
$variant:ident $fields:tt
409447
)*
410448
}
411449
) => {
450+
$(
451+
seek!(@variant_struct $vis $variant $fields);
452+
)*
412453
paste::item! {
413-
$(
414-
#[derive(Debug, Default, Deserialize, Serialize, PartialEq)]
415-
$vis struct $variant($($(#[$field_meta])? pub(super) $ty),*);
416-
)*
417-
418454
#[derive(Debug, Deserialize, Serialize, PartialEq)]
419455
#[serde(untagged)]
420456
$vis enum [<$name Payload>] {
@@ -583,12 +619,16 @@ mod tests {
583619
Id(i32)
584620
New(#[serde(with="ts_microseconds")] chrono::NaiveDateTime, i32)
585621
RecentDownloads(Option<i64>, i32)
622+
NamedId{id: i32}
623+
NamedNew{#[serde(with="ts_microseconds")] dt: chrono::NaiveDateTime, id: i32}
624+
NamedRecentDownloads{ downloads: Option<i64>, id: i32 }
586625
}
587626
}
588627
}
589628

590629
#[test]
591630
fn test_seek_macro_encode_and_decode() {
631+
use chrono::naive::serde::ts_microseconds;
592632
use chrono::{NaiveDate, NaiveDateTime};
593633
use seek::*;
594634

@@ -601,6 +641,7 @@ mod tests {
601641
assert_eq!(decoded, expect);
602642
};
603643

644+
// Tuple struct
604645
let seek = Seek::Id;
605646
let payload = SeekPayload::Id(Id(1234));
606647
let query = format!("seek={}", encode_seek(&payload).unwrap());
@@ -634,13 +675,62 @@ mod tests {
634675
assert_eq!(error.to_string(), "invalid seek parameter");
635676
let response = error.response();
636677
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
678+
679+
// Field struct
680+
let id = 1234;
681+
let seek = Seek::NamedId;
682+
let payload = SeekPayload::NamedId(NamedId { id });
683+
let query = format!("seek={}", encode_seek(&payload).unwrap());
684+
assert_decode_after(seek, &query, Some(payload));
685+
686+
let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2016, 7, 8)
687+
.unwrap()
688+
.and_hms_opt(9, 10, 11)
689+
.unwrap();
690+
let seek = Seek::NamedNew;
691+
let payload = SeekPayload::NamedNew(NamedNew { dt, id });
692+
let query = format!("seek={}", encode_seek(&payload).unwrap());
693+
assert_decode_after(seek, &query, Some(payload));
694+
695+
let downloads = Some(5678);
696+
let seek = Seek::NamedRecentDownloads;
697+
let payload = SeekPayload::NamedRecentDownloads(NamedRecentDownloads { downloads, id });
698+
let query = format!("seek={}", encode_seek(&payload).unwrap());
699+
assert_decode_after(seek, &query, Some(payload));
700+
701+
let seek = Seek::Id;
702+
assert_decode_after(seek, "", None);
703+
704+
let seek = Seek::Id;
705+
let payload = SeekPayload::NamedRecentDownloads(NamedRecentDownloads { downloads, id });
706+
let query = format!("seek={}", encode_seek(payload).unwrap());
707+
let pagination = PaginationOptions::builder()
708+
.enable_seek(true)
709+
.gather(&mock(&query))
710+
.unwrap();
711+
let error = seek.after(&pagination.page).unwrap_err();
712+
assert_eq!(error.to_string(), "invalid seek parameter");
713+
let response = error.response();
714+
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
715+
716+
// Ensures it still encodes compactly with a field struct
717+
#[derive(Debug, Default, Serialize, PartialEq)]
718+
struct NewTuple(
719+
#[serde(with = "ts_microseconds")] chrono::NaiveDateTime,
720+
i32,
721+
);
722+
assert_eq!(
723+
encode_seek(NewTuple(dt, id)).unwrap(),
724+
encode_seek(SeekPayload::NamedNew(NamedNew { dt, id })).unwrap()
725+
);
637726
}
638727

639728
#[test]
640729
fn test_seek_macro_conv() {
641730
use chrono::{NaiveDate, NaiveDateTime};
642731
use seek::*;
643732

733+
// Tuple struct
644734
assert_eq!(Seek::from(SeekPayload::Id(Id(1234))), Seek::Id);
645735

646736
let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2016, 7, 8)
@@ -653,6 +743,31 @@ mod tests {
653743
Seek::from(SeekPayload::RecentDownloads(RecentDownloads(None, 1234))),
654744
Seek::RecentDownloads
655745
);
746+
747+
// Field struct
748+
let id = 1234;
749+
assert_eq!(
750+
Seek::from(SeekPayload::NamedId(NamedId { id })),
751+
Seek::NamedId
752+
);
753+
754+
let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2016, 7, 8)
755+
.unwrap()
756+
.and_hms_opt(9, 10, 11)
757+
.unwrap();
758+
assert_eq!(
759+
Seek::from(SeekPayload::NamedNew(NamedNew { dt, id })),
760+
Seek::NamedNew
761+
);
762+
763+
let downloads = None;
764+
assert_eq!(
765+
Seek::from(SeekPayload::NamedRecentDownloads(NamedRecentDownloads {
766+
downloads,
767+
id
768+
})),
769+
Seek::NamedRecentDownloads
770+
);
656771
}
657772

658773
fn mock(query: &str) -> Request<()> {

0 commit comments

Comments
 (0)