Skip to main content

cli_engine/transport/
mod.rs

1//! HTTP transport helpers for command implementations.
2//!
3//! [`crate::transport::client::HttpClient`] wraps `reqwest` with the
4//! conventions CLI commands usually need: auth injection, default headers,
5//! user-agent handling, structured HTTP errors, idempotent retries, raw
6//! response helpers, multipart helpers, ETag helpers, and GraphQL envelope
7//! decoding.
8
9use std::borrow::Cow;
10
11use serde::{Deserialize, Serialize};
12
13use crate::DetailedError;
14
15/// HTTP client implementation.
16pub mod client;
17/// Request auth injectors.
18pub mod injector;
19
20pub use client::{
21    HttpClient, HttpClientBuilder, NoopTransportLogger, TransportLogEvent, TransportLogger,
22    set_default_user_agent,
23};
24pub use injector::{
25    ApiKeyInjector, AuthInjector, BasicAuthInjector, BearerTokenInjector,
26    ClientCredentialsInjector, CookieInjector, NoopInjector, ProviderBearerInjector, TokenFunc,
27};
28
29/// Structured HTTP error decoded from a backend response.
30#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, thiserror::Error)]
31#[error("{message}")]
32pub struct Error {
33    /// Error code. Backend errors are normalized to `HTTP_<status>`.
34    pub code: String,
35    /// Human-readable backend or transport error message.
36    pub message: String,
37    /// Optional backend system id.
38    #[serde(default, skip_serializing_if = "String::is_empty")]
39    pub system: String,
40    /// Optional request id returned by the backend.
41    #[serde(default, skip_serializing_if = "String::is_empty")]
42    pub request_id: String,
43}
44
45impl DetailedError for Error {
46    fn error_code(&self) -> Cow<'static, str> {
47        Cow::Owned(self.code.clone())
48    }
49
50    fn error_system(&self) -> Option<Cow<'static, str>> {
51        (!self.system.is_empty()).then(|| Cow::Owned(self.system.clone()))
52    }
53
54    fn error_request_id(&self) -> Option<Cow<'static, str>> {
55        (!self.request_id.is_empty()).then(|| Cow::Owned(self.request_id.clone()))
56    }
57}