product-os-request 0.0.55

Product OS : Request provides a fully featured HTTP request library combining elements of reqwest and hyper for async requests with a series of helper methods to allow for easier usage depending upon your needs for one-time or repeat usage.
Documentation
//! HTTP request builder and types
//!
//! This module provides the `ProductOSRequest` type for building HTTP requests.

use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::string::ToString;

use crate::method::Method;
use product_os_http::Request;
use product_os_http_body::BodyBytes;

/// HTTP request builder
///
/// Supports method, URL, query params, headers, and body.
#[cfg(any(feature = "request", feature = "request_std"))]
pub struct ProductOSRequest<DReq: product_os_http_body::Body> {
    /// Absolute request URL (without synthesized query string for hyper backend).
    pub url: String,
    /// HTTP method to execute.
    pub method: Method,
    /// Query parameters to append to the URL.
    pub query: BTreeMap<String, String>,
    /// Request headers keyed by header name.
    pub headers: BTreeMap<String, String>,
    /// Optional request body payload.
    pub body: Option<DReq>,
    /// Optional bearer token for the `Authorization: Bearer ...` header.
    pub bearer_auth: Option<String>,
}

#[cfg(any(feature = "request", feature = "request_std"))]
impl<DReq: product_os_http_body::Body> ProductOSRequest<DReq> {
    /// Create a new request with method and URL
    pub fn new(method: Method, url: &str) -> Self {
        Self {
            url: url.to_string(),
            method,
            query: BTreeMap::new(),
            headers: BTreeMap::new(),
            body: None,
            bearer_auth: None,
        }
    }

    /// Add a header
    pub fn add_header(&mut self, name: &str, value: &str, _is_sensitive: bool) {
        self.headers.insert(name.to_string(), value.to_string());
    }

    /// Add multiple headers.
    ///
    /// This preserves compatibility with older callers that build a batch of
    /// headers before handing the request to the client.
    pub fn add_headers(&mut self, headers: BTreeMap<String, String>, is_sensitive: bool) {
        for (name, value) in headers {
            self.add_header(name.as_str(), value.as_str(), is_sensitive);
        }
    }

    /// Add a query parameter
    pub fn add_param(&mut self, key: String, value: String) {
        self.query.insert(key, value);
    }

    /// Set all query parameters (replaces existing)
    pub fn add_params(&mut self, params: BTreeMap<String, String>) {
        for (k, v) in params {
            self.query.insert(k, v);
        }
    }

    /// Replace query parameters
    pub fn set_query(&mut self, params: BTreeMap<String, String>) {
        self.query = params;
    }

    /// Set the request body
    pub fn set_body(&mut self, data: DReq) {
        self.body = Some(data);
    }

    /// Take the body from the request
    pub fn into_body(self) -> Option<DReq> {
        self.body
    }

    /// Set bearer token for Authorization header
    pub fn bearer_auth(&mut self, token: String) {
        self.bearer_auth = Some(token);
    }
}

#[cfg(any(feature = "request", feature = "request_std"))]
impl ProductOSRequest<BodyBytes> {
    /// Create a ProductOSRequest from an HTTP Request
    pub fn from_request(r: Request<BodyBytes>) -> Self {
        let (parts, body) = r.into_parts();
        let url = parts.uri.to_string();
        let method = crate::method::Method::from_http_method(&parts.method);
        let mut query = BTreeMap::new();
        if let Some(q) = parts.uri.query() {
            for pair in q.split('&') {
                if let Some((k, v)) = pair.split_once('=') {
                    query.insert(k.to_string(), v.to_string());
                }
            }
        }
        let mut headers = BTreeMap::new();
        for (name, value) in parts.headers.iter() {
            if let Ok(v) = value.to_str() {
                headers.insert(name.to_string(), v.to_string());
            }
        }
        let bearer_auth = parts
            .headers
            .get(product_os_http::header::AUTHORIZATION)
            .and_then(|v| v.to_str().ok())
            .filter(|s| s.starts_with("Bearer "))
            .map(|s| s.trim_start_matches("Bearer ").to_string());
        let body = Some(body);
        Self {
            url,
            method,
            query,
            headers,
            body,
            bearer_auth,
        }
    }
}