sellapp-sdk 0.1.1

Official Rust SDK for the SellApp API: manage products, orders, subscriptions, and customers.
// This file is auto-generated by oagen. Do not edit.

use crate::Error;
use std::{collections::HashSet, future::Future};
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PaginationLinks {
    pub first: String,
    pub last: String,
    pub prev: Option<String>,
    pub next: Option<String>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PaginationMetaLink {
    pub url: Option<String>,
    pub label: String,
    pub active: bool,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PaginationMeta {
    pub current_page: i64,
    pub from: Option<i64>,
    pub last_page: i64,
    pub links: Vec<PaginationMetaLink>,
    pub path: String,
    pub per_page: i64,
    pub to: Option<i64>,
    pub total: i64,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Page<T> {
    pub data: Vec<T>,
    pub links: PaginationLinks,
    pub meta: PaginationMeta,
}
impl<T> Page<T> {
    pub fn next_page_number(
        &self,
        base_url: &str,
        operation_path: &str,
    ) -> Result<Option<u64>, Error> {
        if self.meta.current_page >= self.meta.last_page {
            return Ok(None);
        }
        let Some(link) = self.links.next.as_deref() else {
            return Ok(Some((self.meta.current_page + 1) as u64));
        };
        let base =
            reqwest::Url::parse(base_url).map_err(|e| Error::Configuration(e.to_string()))?;
        let next = base
            .join(link)
            .map_err(|e| Error::Serialization(format!("malformed next-page link: {e}")))?;
        if next.scheme() != base.scheme()
            || next.host_str() != base.host_str()
            || next.port_or_known_default() != base.port_or_known_default()
            || next.fragment().is_some()
            || !next.username().is_empty()
            || next.password().is_some()
        {
            return Err(Error::Serialization("unsafe next-page origin".into()));
        }
        let expected = base
            .join(operation_path.trim_start_matches('/'))
            .map_err(|e| Error::Serialization(e.to_string()))?;
        if next.path() != expected.path() {
            return Err(Error::Serialization(
                "next-page operation path changed".into(),
            ));
        }
        let raw = next
            .query_pairs()
            .find(|(key, _)| key == "page")
            .map(|(_, value)| value.into_owned())
            .ok_or_else(|| Error::Serialization("next-page link has no page number".into()))?;
        let page = raw
            .parse::<u64>()
            .map_err(|_| Error::Serialization("malformed next-page number".into()))?;
        if page == 0 {
            return Err(Error::Serialization("page number must be positive".into()));
        }
        Ok(Some(page))
    }
}
pub fn auto_paginate_pages<T, E, F, Fut>(
    mut fetch: F,
) -> impl futures_util::Stream<Item = Result<T, E>>
where
    E: From<Error>,
    F: FnMut(Option<String>) -> Fut,
    Fut: Future<Output = Result<(Vec<T>, Option<String>), E>>,
{
    async_stream::try_stream! {let mut cursor=None;let mut seen=HashSet::new();loop{let(items,next)=fetch(cursor).await?;for item in items{yield item}match next{Some(value)=>{if value.trim().is_empty()||!seen.insert(value.clone()){Err(E::from(Error::Serialization("invalid or repeated pagination cursor".into())))?}cursor=Some(value)},None=>break}}}
}
pub fn auto_paginate_numbered<T, F, Fut>(
    mut fetch: F,
    max_pages: usize,
    max_items: usize,
) -> impl futures_util::Stream<Item = Result<T, Error>>
where
    F: FnMut(u64) -> Fut,
    Fut: Future<Output = Result<(Vec<T>, Option<u64>), Error>>,
{
    async_stream::try_stream! {let(mut page,mut pages,mut items)=(1u64,0usize,0usize);let mut seen=HashSet::new();loop{if pages>=max_pages||items>=max_items{break};if !seen.insert(page){Err(Error::Serialization("pagination cycle detected".into()))?}let(values,next)=fetch(page).await?;pages+=1;for value in values{if items>=max_items{break}items+=1;yield value}match next{Some(value)=>page=value,None=>break}}}
}