use super::PagedCollectionPage;
use crate::{QueryProgress, QueryProgressEvent, QueryProgressState};
pub trait PagedSnapshotRefresh {
type Complete;
type Error;
const PAGE_LIMIT_REASON: &'static str = "max pages reached before API exhaustion";
fn progress(&self) -> crate::PagedQueryProgress;
fn max_pages_reached(&self) -> bool;
fn incomplete_refresh_error(&self, reason: &'static str) -> Self::Error;
fn fetch_next_page(
&mut self,
) -> crate::report::SourceFuture<'_, Result<PagedCollectionPage, Self::Error>>;
fn write_running_attempt(&self) -> Result<(), Self::Error> {
Ok(())
}
fn into_complete(self) -> Self::Complete;
}
pub async fn run_paged_snapshot_refresh_with_progress<Refresh>(
mut refresh: Refresh,
progress: &mut dyn QueryProgress,
) -> Result<Refresh::Complete, Refresh::Error>
where
Refresh: PagedSnapshotRefresh,
{
report_progress(progress, &refresh, QueryProgressState::Running);
loop {
if refresh.max_pages_reached() {
report_progress(progress, &refresh, QueryProgressState::Stopped);
return Err(refresh.incomplete_refresh_error(Refresh::PAGE_LIMIT_REASON));
}
let page = match refresh.fetch_next_page().await.and_then(|page| {
refresh.write_running_attempt()?;
Ok(page)
}) {
Ok(page) => page,
Err(err) => {
report_progress(progress, &refresh, QueryProgressState::Failed);
return Err(err);
}
};
report_progress(progress, &refresh, QueryProgressState::Running);
if page.collection_exhausted {
break;
}
if page.has_no_new_rows() {
report_progress(progress, &refresh, QueryProgressState::Stalled);
return Err(refresh.incomplete_refresh_error(
"page returned no new rows while advertising another cursor",
));
}
}
report_progress(progress, &refresh, QueryProgressState::Complete);
Ok(refresh.into_complete())
}
fn report_progress(
progress: &mut dyn QueryProgress,
refresh: &impl PagedSnapshotRefresh,
state: QueryProgressState,
) {
progress.report(QueryProgressEvent::PagedRefresh {
progress: refresh.progress(),
state,
});
}