taler-rust

GNU Taler code in Rust. Largely core banking integrations.
Log | Files | Refs | Submodules | README | LICENSE

lib.rs (3703B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2026 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 
     17 use std::{borrow::Cow, fmt::Display, str::Utf8Error};
     18 
     19 use http_body_util::Full;
     20 use hyper::{Method, StatusCode, body::Bytes};
     21 use hyper_rustls::ConfigBuilderExt as _;
     22 use hyper_util::rt::TokioExecutor;
     23 use rustls::crypto::CryptoProvider;
     24 use taler_common::error::FmtSource;
     25 use thiserror::Error;
     26 
     27 use crate::headers::HeaderError;
     28 
     29 pub mod builder;
     30 pub mod headers;
     31 pub mod sse;
     32 
     33 pub type Client = hyper_util::client::legacy::Client<
     34     hyper_rustls::HttpsConnector<hyper_util::client::legacy::connect::HttpConnector>,
     35     Full<Bytes>,
     36 >;
     37 
     38 #[derive(Error, Debug)]
     39 /// API call errors
     40 pub enum ClientErr {
     41     #[error("request: {0}")]
     42     Http(#[from] http::Error),
     43     #[error("request query: {0}")]
     44     Query(#[from] serde_urlencoded::ser::Error),
     45     #[error("request JSON body: {0}")]
     46     ReqJson(serde_path_to_error::Error<serde_json::Error>),
     47     #[error("request: {0}")]
     48     ReqTransport(FmtSource<hyper_util::client::legacy::Error>),
     49     #[error("response JSON body: {0}")]
     50     ResJson(serde_path_to_error::Error<serde_json::Error>),
     51     #[error("response txt body: {0}")]
     52     Text(#[from] Utf8Error),
     53     #[error("response form body: {0}")]
     54     Form(#[from] serde_urlencoded::de::Error),
     55     #[error("response headers: {0}")]
     56     Headers(#[from] HeaderError),
     57     #[error("response: {0}")]
     58     ResTransport(String),
     59     #[error("response SSE: {0}")]
     60     Sse(String),
     61 }
     62 
     63 pub fn client() -> Client {
     64     if CryptoProvider::get_default().is_none() {
     65         rustls::crypto::aws_lc_rs::default_provider()
     66             .install_default()
     67             .expect("failed to install the default TLS provider");
     68     }
     69 
     70     // Prepare the TLS client config
     71     let tls = rustls::ClientConfig::builder()
     72         .try_with_platform_verifier()
     73         .expect("failed to setup platform TLS verifier")
     74         .with_no_client_auth();
     75 
     76     // Prepare the HTTPS connector
     77     let https = hyper_rustls::HttpsConnectorBuilder::new()
     78         .with_tls_config(tls)
     79         .https_or_http()
     80         .enable_http1()
     81         .enable_http2()
     82         .build();
     83 
     84     // Build the hyper client from the HTTPS connector.
     85     hyper_util::client::legacy::Client::builder(TokioExecutor::new()).build(https)
     86 }
     87 
     88 #[derive(Debug, Clone)]
     89 pub struct Ctx {
     90     path: Cow<'static, str>,
     91     method: Method,
     92     status: Option<StatusCode>,
     93 }
     94 
     95 impl Ctx {
     96     pub fn wrap<E: std::error::Error>(self, err: E) -> ApiErr<E> {
     97         ApiErr {
     98             ctx: self,
     99             err: Box::new(err),
    100         }
    101     }
    102 }
    103 
    104 impl Display for Ctx {
    105     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    106         let Self {
    107             path,
    108             method,
    109             status,
    110         } = self;
    111         write!(f, "{path} {method}")?;
    112         if let Some(status) = status {
    113             write!(f, " {status}")?;
    114         }
    115         Ok(())
    116     }
    117 }
    118 
    119 #[derive(Debug, Error)]
    120 /// Error happening with api request context
    121 #[error("{ctx} {err}")]
    122 pub struct ApiErr<E: std::error::Error> {
    123     pub ctx: Ctx,
    124     pub err: Box<E>,
    125 }