taler-rust

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

lib.rs (3616B)


      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(FmtSource<hyper::Error>),
     59 }
     60 
     61 pub fn client() -> Client {
     62     if CryptoProvider::get_default().is_none() {
     63         rustls::crypto::aws_lc_rs::default_provider()
     64             .install_default()
     65             .expect("failed to install the default TLS provider");
     66     }
     67 
     68     // Prepare the TLS client config
     69     let tls = rustls::ClientConfig::builder()
     70         .try_with_platform_verifier()
     71         .expect("failed to setup platform TLS verifier")
     72         .with_no_client_auth();
     73 
     74     // Prepare the HTTPS connector
     75     let https = hyper_rustls::HttpsConnectorBuilder::new()
     76         .with_tls_config(tls)
     77         .https_or_http()
     78         .enable_http1()
     79         .enable_http2()
     80         .build();
     81 
     82     // Build the hyper client from the HTTPS connector.
     83     hyper_util::client::legacy::Client::builder(TokioExecutor::new()).build(https)
     84 }
     85 
     86 #[derive(Debug, Clone)]
     87 pub struct Ctx {
     88     path: Cow<'static, str>,
     89     method: Method,
     90     status: Option<StatusCode>,
     91 }
     92 
     93 impl Ctx {
     94     pub fn wrap<E: std::error::Error>(self, err: E) -> ApiErr<E> {
     95         ApiErr { ctx: self, err }
     96     }
     97 }
     98 
     99 impl Display for Ctx {
    100     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    101         let Self {
    102             path,
    103             method,
    104             status,
    105         } = self;
    106         write!(f, "{path} {method}")?;
    107         if let Some(status) = status {
    108             write!(f, " {status}")?;
    109         }
    110         Ok(())
    111     }
    112 }
    113 
    114 #[derive(Debug, Error)]
    115 /// Error happening with api request context
    116 #[error("{ctx} {err}")]
    117 pub struct ApiErr<E: std::error::Error> {
    118     pub ctx: Ctx,
    119     pub err: E,
    120 }