cashweb_relay_client/
lib.rs

1#![warn(
2    missing_debug_implementations,
3    missing_docs,
4    rust_2018_idioms,
5    unreachable_pub
6)]
7
8//! `cashweb-relay-client` is a library providing [`RelayClient`] which allows
9//! interaction with specific relay server.
10
11pub mod services;
12
13use std::{error, fmt};
14
15pub use hyper::{
16    client::{connect::Connect, HttpConnector},
17    Uri,
18};
19
20use hyper::client::Client as HyperClient;
21use hyper::http::uri::InvalidUri;
22use secp256k1::key::PublicKey;
23use thiserror::Error;
24use tower_service::Service;
25use tower_util::ServiceExt;
26
27use relay::Profile;
28use services::*;
29
30/// RelayClient allows queries to specific relay servers.
31#[derive(Clone, Debug)]
32pub struct RelayClient<S> {
33    inner_client: S,
34}
35
36impl<S> RelayClient<S> {
37    /// Create a new client from a service.
38    pub fn from_service(service: S) -> Self {
39        Self {
40            inner_client: service,
41        }
42    }
43}
44
45impl Default for RelayClient<HyperClient<HttpConnector>> {
46    fn default() -> Self {
47        Self {
48            inner_client: HyperClient::new(),
49        }
50    }
51}
52
53impl RelayClient<HyperClient<HttpConnector>> {
54    /// Create a new HTTP client.
55    pub fn new() -> Self {
56        Default::default()
57    }
58}
59
60/// Error associated with sending a request to a relay server.
61#[derive(Debug, Error)]
62pub enum RelayError<E: fmt::Debug + fmt::Display + error::Error + 'static> {
63    /// Invalid URI.
64    #[error(transparent)]
65    Uri(InvalidUri),
66    /// Error executing the service method.
67    #[error("failed to execute service method: {0}")]
68    Error(#[from] E),
69}
70
71/// A [`Profile`] paired with its [`PublicKey`].
72#[derive(Clone, Debug)]
73pub struct ProfilePackage {
74    /// Public key of the metadata.
75    pub public_key: PublicKey,
76    /// The profile.
77    pub profile: Profile,
78}
79
80impl<S> RelayClient<S>
81where
82    Self: Service<(Uri, GetProfile), Response = ProfilePackage>,
83    Self: Sync + Clone + Send + 'static,
84    <Self as Service<(Uri, GetProfile)>>::Future: Send + Sync + 'static,
85    <Self as Service<(Uri, GetProfile)>>::Error: fmt::Debug + fmt::Display + error::Error,
86{
87    /// Get [`Profile`] from a server. The result is wrapped in [`ProfilePackage`].
88    pub async fn get_profile(
89        &self,
90        keyserver_url: &str,
91        address: &str,
92    ) -> Result<ProfilePackage, RelayError<<Self as Service<(Uri, GetProfile)>>::Error>> {
93        // Construct URI
94        let full_path = format!("{}/profiles/{}", keyserver_url, address);
95        let uri: Uri = full_path.parse().map_err(RelayError::Uri)?;
96
97        // Construct request
98        let request = (uri, GetProfile);
99
100        self.clone()
101            .oneshot(request)
102            .await
103            .map_err(RelayError::Error)
104    }
105}
106
107impl<S> RelayClient<S>
108where
109    Self: Service<(Uri, PutProfile), Response = ()>,
110    Self: Sync + Clone + Send + 'static,
111    <Self as Service<(Uri, PutProfile)>>::Future: Send + Sync + 'static,
112    <Self as Service<(Uri, PutProfile)>>::Error: fmt::Debug + fmt::Display + error::Error,
113{
114    /// Put a [`Profile`] to a relay server.
115    pub async fn put_profile(
116        &self,
117        relay_url: &str,
118        address: &str,
119        profile: Profile,
120        token: String,
121    ) -> Result<(), RelayError<<Self as Service<(Uri, PutProfile)>>::Error>> {
122        // Construct URI
123        let full_path = format!("{}/profiles/{}", relay_url, address);
124        let uri: Uri = full_path.parse().map_err(RelayError::Uri)?;
125
126        // Construct request
127        let request = (uri, PutProfile { token, profile });
128
129        // Get response
130        self.clone()
131            .oneshot(request)
132            .await
133            .map_err(RelayError::Error)
134    }
135}