reasoninglayer 0.2.1

Rust client SDK for the Reasoning Layer API
Documentation
//! Types shared across all resources: [`ApiResponse`], [`RateLimitInfo`], [`RequestOptions`].
//!
//! [`RequestOptions`] allows per-call overrides of client defaults. Pass `None` to use client
//! defaults. For cancellation, wrap the call in `tokio::select!` or `tokio::time::timeout` — the
//! returned future is cancellable by being dropped.

use std::time::Duration;

use reqwest::header::HeaderMap;
use reqwest::StatusCode;

use crate::error::parse_numeric_header;

/// Response metadata + the parsed body. Returned from resource methods' `*_with_metadata` variants.
#[derive(Debug, Clone)]
pub struct ApiResponse<T> {
    /// The parsed response body.
    pub data: T,
    /// HTTP status code.
    pub status: StatusCode,
    /// Raw response headers.
    pub headers: HeaderMap,
    /// Rate limit information, if any `X-RateLimit-*` or `Retry-After` headers were present.
    pub rate_limit: Option<RateLimitInfo>,
}

/// Rate limit information parsed from `X-RateLimit-*` and `Retry-After` response headers.
#[derive(Debug, Clone, Copy, Default)]
pub struct RateLimitInfo {
    /// Maximum number of requests allowed in the current window.
    pub limit: u64,
    /// Requests remaining in the current window.
    pub remaining: u64,
    /// Seconds to wait before retrying, or `None` if not rate-limited.
    pub retry_after: Option<u64>,
}

impl RateLimitInfo {
    /// Parse rate limit headers. Returns `None` if none of the relevant headers are present.
    pub(crate) fn from_headers(headers: &HeaderMap) -> Option<Self> {
        let limit = parse_numeric_header(headers, "x-ratelimit-limit");
        let remaining = parse_numeric_header(headers, "x-ratelimit-remaining");
        let retry_after = parse_numeric_header(headers, "retry-after");
        if limit.is_none() && remaining.is_none() && retry_after.is_none() {
            return None;
        }
        Some(Self {
            limit: limit.unwrap_or(0),
            remaining: remaining.unwrap_or(0),
            retry_after,
        })
    }
}

/// Per-call overrides for a single request. Every resource method accepts `Option<RequestOptions>`.
///
/// `X-Tenant-Id` is never overridable per-call — changing tenants requires a new client.
///
/// Cancellation: wrap the call in `tokio::select!` or `tokio::time::timeout`; the returned future
/// is cancellable by being dropped.
#[derive(Debug, Clone, Default)]
pub struct RequestOptions {
    /// Override `X-Namespace-Id` for this request.
    pub namespace_id: Option<String>,
    /// Override `X-User-Id` for this request (admin impersonation).
    pub user_id: Option<String>,
    /// Per-call timeout. Overrides [`ClientConfig::timeout`](crate::ClientConfig::timeout).
    pub timeout: Option<Duration>,
    /// Per-call retry count. `Some(0)` disables retries for this request.
    pub retries: Option<u32>,
}

impl RequestOptions {
    /// A fresh, empty set of options.
    pub fn new() -> Self {
        Self::default()
    }

    /// Override the namespace ID.
    #[must_use]
    pub fn with_namespace_id(mut self, namespace_id: impl Into<String>) -> Self {
        self.namespace_id = Some(namespace_id.into());
        self
    }

    /// Override the user ID.
    #[must_use]
    pub fn with_user_id(mut self, user_id: impl Into<String>) -> Self {
        self.user_id = Some(user_id.into());
        self
    }

    /// Override the request timeout.
    #[must_use]
    pub fn with_timeout(mut self, timeout: Duration) -> Self {
        self.timeout = Some(timeout);
        self
    }

    /// Override the retry count for this request.
    #[must_use]
    pub fn with_retries(mut self, retries: u32) -> Self {
        self.retries = Some(retries);
        self
    }
}