use std::fmt;
use std::future::Future;
use async_stream::try_stream;
use futures::stream;
use futures::StreamExt;
use serde::de::DeserializeOwned;
use crate::JQuantsBuilder;
use crate::JQuantsError;
pub trait HasPaginationKey {
fn get_pagination_key(&self) -> Option<&str>;
}
pub trait MergePage: Sized {
fn merge_page(
page: Result<Vec<Self>, crate::JQuantsError>,
) -> Result<Self, crate::JQuantsError>;
}
pub trait Paginatable<R: DeserializeOwned + fmt::Debug + HasPaginationKey + MergePage>:
JQuantsBuilder<R> + Clone
{
fn pagination_key(self, pagination_key: impl Into<String>) -> Self;
fn fetch_pages_stream(self) -> impl stream::Stream<Item = Result<R, JQuantsError>> {
let stream = try_stream! {
let mut builder = self.clone();
loop {
let response = builder.send_ref().await?;
let next_pagination_key = response.get_pagination_key();
if let Some(key) = next_pagination_key {
builder = builder.pagination_key(key.to_string());
yield response;
continue;
} else {
yield response;
break;
}
}
};
Box::pin(stream)
}
fn fetch_all(self) -> impl Future<Output = Result<Vec<R>, JQuantsError>> {
async {
let results: Vec<Result<R, JQuantsError>> = self.fetch_pages_stream().collect().await;
let mut final_results = Vec::new();
for result in results {
match result {
Ok(value) => final_results.push(value),
Err(e) => return Err(e),
}
}
Ok(final_results)
}
}
fn fetch_all_and_merge(self) -> impl Future<Output = Result<R, JQuantsError>> {
async {
let results = self.fetch_all().await;
R::merge_page(results)
}
}
}