polyoxide-core 0.48.0

Core utilities and shared types for the polyoxide venue clients: the HTTP send loop and its hooks, window quotas, capacity buckets and holds, errors and macros
Documentation

polyoxide-core

Core infrastructure crate for the polyoxide workspace. Provides the HTTP client and its one send loop, the hooks a venue gives that loop, error types, rate limiting, request building, and macros that every venue crate builds on. It names no venue: each venue's own rules, such as its retry policy, its rate limit tables and its signing, live in that venue's crate.

This is an internal crate. End users should depend on polyoxide instead.

What's inside

Module Purpose
client HttpClient / HttpClientBuilder -- configurable client over an injected or default transport, with base URL, connection pooling, concurrency limiting, and 429 retry logic
transport HttpTransport, the seam the send loop sends through, with HttpRequest, TransportError and TransportKind, and ReqwestTransport, the native default
response Response -- a transport's answer, an owned head and a lazy body read by bytes, text and json
error ApiError -- shared error enum (Response, Validation, Transport, Serialization, Url, Refused, Sign), classed through polyoxide_venue::Classify; ErrorResponse -- an unsuccessful response read whole (status, headers, body, message, Retry-After), which ApiError::Response carries
hooks Throttle, RetryPolicy and Authenticator -- the hooks a venue gives the send loop -- with the types they take (RequestMeta, ResponseMeta, AttemptInfo, RequestParts, Decision, Cost, Charge, Refused), NoThrottle and DefaultRetryPolicy
send HttpClient::send, core's one send loop, and decode_json, which logs a decode failure once
hold Hold -- the extend-only deadline every layer of one throttle waits out, with an optional ceiling
capacity CapacityBucket -- a token bucket holding a published capacity, charging integer costs, refusing a cost it can never hold, resizable in place
request Request<T, E>, QueryBuilder trait, RequestError marker trait (any From<ApiError> + Debug type, whose From is its decode) -- the one request builder: method, query, JSON body, authenticator and throttle costs, sent on the send loop with automatic deserialization
rate_limit WindowQuotaTable, which builds a RateLimiter from a general bucket and per-endpoint rows (shared buckets, prefix or exact matching, scoped by method), with effective_quota() and rows() for inspection; RetryConfig for exponential backoff with jitter
macros client_config_setters!, namespaces! and query_setters! -- a builder's transport setters, a client's namespace accessors, and a request builder's query setters
keychain OS credential storage via keyring (feature-gated behind keychain) -- the StoredCredential trait a venue implements for each kind of credential it stores (its service, its Fields, which are secret, and its checks), with the generic store, show and delete_all over it, the per-key get, set and delete, and KeychainError

Error hierarchy

Each venue crate defines its own error enum, one per module, with an Api(ApiError) variant. An unsuccessful response reaches it as ApiError::Response, which carries the whole response (ErrorResponse: status, headers, body, message and Retry-After), and the crate's From<ApiError> is its one decode: a ? on core's error anywhere reads the venue's own body shape there. Transport, URL and JSON failures enter the same way, as ApiError::Transport, Url and Serialization:

use polyoxide_core::ApiError;
use thiserror::Error;

#[derive(Error, Debug)]
#[non_exhaustive]
pub enum MyCrateError {
    #[error(transparent)]
    Api(ApiError),
    #[error("venue refused: {0}")]
    Venue(String),
}

impl From<ApiError> for MyCrateError {
    fn from(err: ApiError) -> Self {
        match err {
            // The venue's own body shape becomes its own variant.
            ApiError::Response(response) if response.body.starts_with(r#"{"venue""#) => {
                Self::Venue(response.message)
            }
            other => Self::Api(other),
        }
    }
}

Key exports

# #![allow(unused_imports)]
// HTTP client
use polyoxide_core::{HttpClient, HttpClientBuilder, DEFAULT_TIMEOUT_MS, DEFAULT_POOL_SIZE};

// The transport it sends on
use polyoxide_core::{HttpRequest, HttpTransport, Response, TransportError, TransportKind};

// Errors
use polyoxide_core::{ApiError, ErrorResponse};

// Request building
use polyoxide_core::{Request, QueryBuilder, RequestError};

// Rate limiting & retry
use polyoxide_core::{CapacityBucket, Hold, RateLimiter, RetryConfig, WindowQuotaTable};

// The send loop's hooks
use polyoxide_core::{Authenticator, DefaultRetryPolicy, RetryPolicy, Throttle};

Installation

[dependencies]
polyoxide-core = "0.48"

License

Licensed under either of MIT or Apache-2.0 at your option.