@@ -87,13 +87,29 @@ pub(crate) fn opt_str<'a>(body: &'a Value, field: &str) -> Option<&'a str> {
8787/// an empty page — restarting at the beginning would silently re-list page 1
8888/// and risk an infinite pagination loop for the client.
8989pub ( crate ) fn paginate_arns (
90+ arns : Vec < String > ,
91+ next_token : & str ,
92+ max_results : usize ,
93+ ) -> ( Vec < String > , Option < String > ) {
94+ paginate_arns_dir ( arns, next_token, max_results, false )
95+ }
96+
97+ /// Like [`paginate_arns`] but honors a sort direction. `descending` reverses the
98+ /// canonical (ascending, deduped) order and adjusts the resume cursor
99+ /// comparison so DESC pages advance correctly instead of being silently
100+ /// re-sorted back to ASC.
101+ pub ( crate ) fn paginate_arns_dir (
90102 mut arns : Vec < String > ,
91103 next_token : & str ,
92104 max_results : usize ,
105+ descending : bool ,
93106) -> ( Vec < String > , Option < String > ) {
94107 use base64:: Engine ;
95108 arns. sort ( ) ;
96109 arns. dedup ( ) ;
110+ if descending {
111+ arns. reverse ( ) ;
112+ }
97113 let cursor = if next_token. is_empty ( ) {
98114 None
99115 } else {
@@ -106,9 +122,17 @@ pub(crate) fn paginate_arns(
106122 0
107123 } else {
108124 match & cursor {
125+ // Resume just past the cursor. For ASC the next element is the first
126+ // greater than the cursor; for DESC it's the first less than it.
109127 Some ( c) => arns
110128 . iter ( )
111- . position ( |a| a. as_str ( ) > c. as_str ( ) )
129+ . position ( |a| {
130+ if descending {
131+ a. as_str ( ) < c. as_str ( )
132+ } else {
133+ a. as_str ( ) > c. as_str ( )
134+ }
135+ } )
112136 . unwrap_or ( arns. len ( ) ) ,
113137 // Non-empty but undecodable token: a cursor we never emitted.
114138 // Treat as past the end (empty page) rather than restarting at 0.
@@ -1571,7 +1595,21 @@ pub(crate) fn task_protection_json(task: &Task) -> Value {
15711595
15721596#[ cfg( test) ]
15731597mod pagination_tests {
1574- use super :: paginate_arns;
1598+ use super :: { paginate_arns, paginate_arns_dir} ;
1599+
1600+ #[ test]
1601+ fn paginate_arns_dir_descending_pages_in_reverse ( ) {
1602+ let all: Vec < String > = ( 0 ..5 ) . map ( |i| format ! ( "arn:{i}" ) ) . collect ( ) ;
1603+ // DESC page 1: highest first.
1604+ let ( p1, next) = paginate_arns_dir ( all. clone ( ) , "" , 2 , true ) ;
1605+ assert_eq ! ( p1, vec![ "arn:4" , "arn:3" ] ) ;
1606+ let token = next. expect ( "nextToken after DESC page 1" ) ;
1607+ // DESC page 2 continues downward, not silently re-sorted to ASC.
1608+ let ( p2, next2) = paginate_arns_dir ( all. clone ( ) , & token, 2 , true ) ;
1609+ assert_eq ! ( p2, vec![ "arn:2" , "arn:1" ] ) ;
1610+ let ( p3, _) = paginate_arns_dir ( all, & next2. unwrap ( ) , 2 , true ) ;
1611+ assert_eq ! ( p3, vec![ "arn:0" ] ) ;
1612+ }
15751613
15761614 #[ test]
15771615 fn paginate_arns_key_cursor_is_stable_across_delete ( ) {
0 commit comments