archive-it-client 0.2.0

Rust client for Archive-It's partner API and WASAPI
Documentation
use std::pin::pin;

use futures_core::Stream;
use futures_util::TryStreamExt;
use tokio::sync::OnceCell;

use crate::http::{Transport, paginated};
use crate::models::partner::{Account, Collection};
use crate::public::CollectionsQuery;
use crate::{Config, Error, PageOpts};

pub struct PartnerClient {
    transport: Transport,
    self_id: OnceCell<u64>,
}

impl PartnerClient {
    pub fn new(username: impl Into<String>, password: impl Into<String>) -> Result<Self, Error> {
        Self::with_config(username, password, Config::api())
    }

    pub fn with_config(
        username: impl Into<String>,
        password: impl Into<String>,
        cfg: Config,
    ) -> Result<Self, Error> {
        Ok(Self {
            transport: Transport::new(cfg, Some((username.into(), password.into())))?,
            self_id: OnceCell::new(),
        })
    }

    pub async fn my_account(&self) -> Result<Account, Error> {
        let accounts: Vec<Account> = self.transport.get_json("account", &()).await?;
        accounts.into_iter().next().ok_or(Error::Empty)
    }

    pub async fn list_collections(&self, opts: PageOpts) -> Result<Vec<Collection>, Error> {
        let me = self.cached_self_id().await?;
        self.transport
            .get_json("collection", &CollectionsQuery::new(Some(me), opts))
            .await
    }

    pub fn collections(&self) -> impl Stream<Item = Result<Collection, Error>> + Send + '_ {
        paginated(move |opts| self.list_collections(opts))
    }

    pub async fn get_collection(&self, id: u64) -> Result<Option<Collection>, Error> {
        let mut stream = pin!(self.collections());
        while let Some(c) = stream.try_next().await? {
            if c.id == id {
                return Ok(Some(c));
            }
        }
        Ok(None)
    }

    async fn cached_self_id(&self) -> Result<u64, Error> {
        self.self_id
            .get_or_try_init(|| async { self.my_account().await.map(|a| a.id) })
            .await
            .copied()
    }
}