use crate::{
api::ApiProvider, config::Config, error::FavCoreError, meta::Meta, res::ResSet, FavCoreResult,
};
use core::future::Future;
use reqwest::{Client, Response};
#[allow(missing_docs)]
#[trait_variant::make(Operations: Send)]
pub trait LocalOperations<K>: ApiProvider<K> + Config
where
K: Send,
{
async fn login(&mut self) -> FavCoreResult<()>;
async fn logout(&mut self) -> FavCoreResult<()>;
async fn fetch(&self, resource: &mut impl Meta) -> FavCoreResult<()>;
async fn pull(&self, resource: &mut impl Meta) -> FavCoreResult<()>;
fn client(&self) -> &'static Client {
use std::sync::OnceLock;
let headers = self.headers();
static CLIENT: OnceLock<Client> = OnceLock::new();
CLIENT.get_or_init(|| Client::builder().default_headers(headers).build().unwrap())
}
fn request(
&self,
api_kind: K,
params: impl IntoIterator<Item = &'static str> + Send,
) -> impl Future<Output = FavCoreResult<Response>> {
async {
let client = self.client();
let api = self.api(api_kind);
let resp = client
.request(
api.method(),
api.url(api.params().iter().copied().zip(params).collect()),
)
.send()
.await
.unwrap();
Ok(resp)
}
}
}
pub trait LocalOperationsExt<K>: LocalOperations<K>
where
K: Send,
{
fn fetch_all(&self, resources: &mut impl ResSet) -> impl Future<Output = FavCoreResult<()>> {
async {
for r in resources.iter_mut() {
if let Err(e) = self.fetch(r).await {
match e {
FavCoreError::Cancel => break,
_ => println!("{e}"),
}
}
}
Ok(())
}
}
fn pull_all(&self, resources: &mut impl ResSet) -> impl Future<Output = FavCoreResult<()>> {
async {
for r in resources.iter_mut() {
if let Err(e) = self.pull(r).await {
match e {
FavCoreError::Cancel => break,
_ => println!("{e}"),
}
}
}
Ok(())
}
}
}
impl<T: LocalOperations<K>, K: Send> LocalOperationsExt<K> for T {}
pub trait OperationsExt<K>: Operations<K>
where
K: Send + 'static,
{
fn fetch_all(
&'static self,
resources: &'static mut impl ResSet,
) -> impl Future<Output = FavCoreResult<()>> {
async {
let mut rs = resources.iter_mut();
loop {
let batch: Vec<_> = rs.by_ref().take(10).collect();
if batch.is_empty() {
break;
}
let jhs: Vec<_> = batch
.into_iter()
.map(|r| tokio::spawn(self.fetch(r)))
.collect();
for jh in jhs {
if let Err(e) = jh.await.unwrap() {
println!("{e}");
}
}
}
Ok(())
}
}
fn pull_all(
&'static self,
resources: &'static mut impl ResSet,
) -> impl Future<Output = FavCoreResult<()>> {
async {
let mut rs = resources.iter_mut();
loop {
let batch: Vec<_> = rs.by_ref().take(10).collect();
if batch.is_empty() {
break;
}
let jhs: Vec<_> = batch
.into_iter()
.map(|r| tokio::spawn(self.pull(r)))
.collect();
for jh in jhs {
if let Err(e) = jh.await.unwrap() {
match e {
FavCoreError::Cancel => {
println!("{e}");
break;
}
_ => println!("{e}"),
}
}
}
}
Ok(())
}
}
}
impl<T: Operations<K>, K: Send + 'static> OperationsExt<K> for T {}