1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
use jsonrpsee::core::client::{ClientT, Subscription, SubscriptionClientT};
use jsonrpsee::http_client::{HttpClient, HttpClientBuilder};
use jsonrpsee::types::ParamsSer;
use jsonrpsee::ws_client::{WsClient, WsClientBuilder};

use serde::de::DeserializeOwned;

use crate::error::*;

#[derive(Debug)]
enum InnerRpcClient {
  Ws(WsClient),
  Http(HttpClient),
}

#[derive(Debug)]
pub struct RpcClient {
  client: InnerRpcClient,
}

impl RpcClient {
  pub async fn new(url: &str) -> Result<Self> {
    if url.starts_with("Http") {
      Self::new_http(url)
    } else if url.starts_with("ws") {
      Self::new_ws(url).await
    } else {
      Err(Error::RpcClient(format!("Unsupported url: {url}")))
    }
  }

  async fn new_ws(url: &str) -> Result<Self> {
    let client = WsClientBuilder::default().build(&url).await?;
    Ok(Self {
      client: InnerRpcClient::Ws(client),
    })
  }

  fn new_http(url: &str) -> Result<Self> {
    let client = HttpClientBuilder::default().build(&url)?;
    Ok(Self {
      client: InnerRpcClient::Http(client),
    })
  }

  pub async fn subscribe<'a, Notif>(
    &self,
    subscribe_method: &'a str,
    params: Option<ParamsSer<'a>>,
    unsubscribe_method: &'a str,
  ) -> Result<Subscription<Notif>>
  where
    Notif: DeserializeOwned,
  {
    Ok(match &self.client {
      InnerRpcClient::Ws(ws) => {
        ws.subscribe(subscribe_method, params, unsubscribe_method)
          .await
      }
      InnerRpcClient::Http(http) => {
        http
          .subscribe(subscribe_method, params, unsubscribe_method)
          .await
      }
    }?)
  }

  pub async fn request<'a, R>(&self, method: &'a str, params: Option<ParamsSer<'a>>) -> Result<R>
  where
    R: DeserializeOwned,
  {
    Ok(match &self.client {
      InnerRpcClient::Ws(ws) => ws.request(method, params).await,
      InnerRpcClient::Http(http) => http.request(method, params).await,
    }?)
  }

  pub async fn batch_request<'a, R>(
    &self,
    batch: Vec<(&'a str, Option<ParamsSer<'a>>)>,
  ) -> Result<Vec<R>>
  where
    R: DeserializeOwned + Default + Clone,
  {
    Ok(match &self.client {
      InnerRpcClient::Ws(ws) => ws.batch_request(batch).await,
      InnerRpcClient::Http(http) => http.batch_request(batch).await,
    }?)
  }
}