diff --git a/sdk/core/azure_core/CHANGELOG.md b/sdk/core/azure_core/CHANGELOG.md
index 6cf68347b9..b5c9bbe6b5 100644
--- a/sdk/core/azure_core/CHANGELOG.md
+++ b/sdk/core/azure_core/CHANGELOG.md
@@ -4,6 +4,7 @@
### Features Added
+- Added `ItemIterator::continuation_token()` and `with_continuation_token()` to resume paging items. The current page is restarted until _after_ all items have been iterated.
- Added `Response::to_raw_response()` function to create a `RawResponse` from cloned data.
- Added `UrlExt::append_path()`.
- Implemented `IntoFuture` for a `Poller`. Call `await` on a Poller to get the final model, or `into_stream()` to get a `futures::Stream` to poll the operation manually.
@@ -25,6 +26,8 @@
### Bugs Fixed
+- `ItemIterator::into_pages()` now properly supports resuming from the current page until _after_ all items have been iterated.
+
### Other Changes
## 0.29.1 (2025-10-06)
diff --git a/sdk/core/azure_core/src/http/pager.rs b/sdk/core/azure_core/src/http/pager.rs
index 7b85a6244c..b352b38056 100644
--- a/sdk/core/azure_core/src/http/pager.rs
+++ b/sdk/core/azure_core/src/http/pager.rs
@@ -3,7 +3,10 @@
//! Types and methods for pageable responses.
-use crate::http::{headers::HeaderName, response::Response, DeserializeWith, Format, JsonFormat};
+use crate::{
+ error::ErrorKind,
+ http::{headers::HeaderName, response::Response, Context, DeserializeWith, Format, JsonFormat},
+};
use async_trait::async_trait;
use futures::{stream::unfold, FutureExt, Stream};
use std::{
@@ -199,6 +202,13 @@ where
/// ```
pub type Pager
= ItemIterator>;
+/// Options for configuring the behavior of a [`Pager`].
+#[derive(Clone, Debug, Default)]
+pub struct PagerOptions<'a> {
+ /// Context for HTTP requests made by the [`Pager`].
+ pub context: Context<'a>,
+}
+
#[cfg(not(target_arch = "wasm32"))]
type BoxedStream = Box> + Send>;
@@ -262,6 +272,8 @@ type BoxedStream = Box>>;
pub struct ItemIterator {
#[pin]
stream: Pin>,
+ continuation_token: Option,
+ next_token: Arc>>,
current: Option,
}
@@ -270,7 +282,7 @@ impl ItemIterator {
///
/// This method expect a callback that accepts a single [`PagerState`] parameter, and returns a [`PagerResult`] value asynchronously.
/// The `C` type parameter is the type of the next link/continuation token. It may be any [`Send`]able type.
- /// The result will be an asynchronous stream of [`Result`](typespec::Result) values.
+ /// The result will be an asynchronous stream of [`Result`](crate::Result) values.
///
/// The first time your callback is called, it will be called with [`Option::None`], indicating no next link/continuation token is present.
///
@@ -302,7 +314,7 @@ impl ItemIterator {
/// }
/// let url = "https://example.com/my_paginated_api".parse().unwrap();
/// let mut base_req = Request::new(url, Method::Get);
- /// let pager = ItemIterator::from_callback(move |next_link: PagerState| {
+ /// let pager = ItemIterator::from_callback(move |next_link: PagerState, ctx| {
/// // The callback must be 'static, so you have to clone and move any values you want to use.
/// let pipeline = pipeline.clone();
/// let api_version = api_version.clone();
@@ -321,7 +333,7 @@ impl ItemIterator {
/// .append_pair("api-version", &api_version);
/// }
/// let resp = pipeline
- /// .send(&Context::new(), &mut req, None)
+ /// .send(&ctx, &mut req, None)
/// .await?;
/// let (status, headers, body) = resp.deconstruct();
/// let result: ListItemsResult = json::from_json(&body)?;
@@ -334,7 +346,7 @@ impl ItemIterator {
/// None => PagerResult::Done { response: resp }
/// })
/// }
- /// });
+ /// }, None);
/// ```
///
/// To page results using headers:
@@ -357,7 +369,7 @@ impl ItemIterator {
/// }
/// let url = "https://example.com/my_paginated_api".parse().unwrap();
/// let mut base_req = Request::new(url, Method::Get);
- /// let pager = ItemIterator::from_callback(move |continuation| {
+ /// let pager = ItemIterator::from_callback(move |continuation, ctx| {
/// // The callback must be 'static, so you have to clone and move any values you want to use.
/// let pipeline = pipeline.clone();
/// let mut req = base_req.clone();
@@ -366,25 +378,38 @@ impl ItemIterator {
/// req.insert_header("x-ms-continuation", continuation);
/// }
/// let resp: Response = pipeline
- /// .send(&Context::new(), &mut req, None)
+ /// .send(&ctx, &mut req, None)
/// .await?
/// .into();
/// Ok(PagerResult::from_response_header(resp, &HeaderName::from_static("x-next-continuation")))
/// }
- /// });
+ /// }, None);
/// ```
pub fn from_callback<
// This is a bit gnarly, but the only thing that differs between the WASM/non-WASM configs is the presence of Send bounds.
- #[cfg(not(target_arch = "wasm32"))] C: AsRef + Send + 'static,
- #[cfg(not(target_arch = "wasm32"))] F: Fn(PagerState) -> Fut + Send + 'static,
+ #[cfg(not(target_arch = "wasm32"))] C: AsRef + FromStr + Send + 'static,
+ #[cfg(not(target_arch = "wasm32"))] F: Fn(PagerState, Context<'static>) -> Fut + Send + 'static,
#[cfg(not(target_arch = "wasm32"))] Fut: Future