1
1
use crate :: util:: Result ;
2
+ use futures:: { future, stream:: SplitSink , StreamExt } ;
2
3
use futures_util:: SinkExt ;
3
- use serde:: Serialize ;
4
+ use serde:: { Deserialize , Serialize } ;
5
+ use std:: { cell:: RefCell , collections:: HashMap , pin:: Pin , rc:: Rc } ;
4
6
use tokio:: net:: TcpStream ;
7
+ use tokio_stream:: Stream ;
5
8
use tokio_tungstenite:: {
6
9
connect_async, tungstenite:: protocol:: Message , MaybeTlsStream , WebSocketStream ,
7
10
} ;
8
11
use uuid:: Uuid ;
9
- use xrpl_api:: Request ;
12
+ use xrpl_api:: { AccountInfoResponse , LedgerClosedEvent , Request } ;
10
13
11
14
// https://xrpl.org/public-servers.html
12
15
@@ -21,15 +24,71 @@ pub const DEFAULT_WS_URL: &str = XRPL_CLUSTER_MAINNET_WS_URL;
21
24
22
25
// #TODO extract Connection
23
26
27
+ #[ derive( Serialize , Deserialize , Debug ) ]
28
+ pub enum TypedMessage {
29
+ AccountInfo ( AccountInfoResponse ) ,
30
+ LedgerClosed ( LedgerClosedEvent ) ,
31
+ Other ( String ) ,
32
+ }
33
+
24
34
/// A WebSocket client for the XRP Ledger.
25
35
pub struct Client {
26
- pub stream : WebSocketStream < MaybeTlsStream < TcpStream > > ,
36
+ sender : SplitSink < WebSocketStream < MaybeTlsStream < TcpStream > > , Message > ,
37
+ requests : Rc < RefCell < HashMap < String , String > > > ,
38
+ pub messages : Pin < Box < dyn Stream < Item = Result < TypedMessage > > > > ,
27
39
}
28
40
29
41
impl Client {
30
42
pub async fn connect ( url : & str ) -> Result < Self > {
31
43
let ( stream, _response) = connect_async ( url) . await ?;
32
- Ok ( Self { stream } )
44
+ let ( sender, receiver) = stream. split ( ) ;
45
+ let requests: Rc < RefCell < HashMap < String , String > > > = Rc :: new ( RefCell :: new ( HashMap :: new ( ) ) ) ;
46
+
47
+ let cloned_requests = requests. clone ( ) ;
48
+ let receiver = receiver
49
+ . map ( move |msg| {
50
+ if let Message :: Text ( string) = msg. unwrap ( ) {
51
+ let mut value: serde_json:: Value = serde_json:: from_str ( & string) . unwrap ( ) ;
52
+
53
+ if let Some ( id) = value[ "id" ] . as_str ( ) {
54
+ // If the message contains an id field it's a response to
55
+ // an RPC request.
56
+ if let Some ( method) = requests. borrow_mut ( ) . get ( id) {
57
+ let result = value[ "result" ] . take ( ) ;
58
+ match method. as_str ( ) {
59
+ "account_info" => Ok ( Some ( TypedMessage :: AccountInfo (
60
+ serde_json:: from_value ( result) ?,
61
+ ) ) ) ,
62
+ _ => Ok ( Some ( TypedMessage :: Other ( string) ) ) ,
63
+ }
64
+ } else {
65
+ Ok ( Some ( TypedMessage :: Other ( string) ) )
66
+ }
67
+ } else {
68
+ // If the message has no id field, it's a subscription event.
69
+
70
+ if let Some ( event_type) = value[ "type" ] . as_str ( ) {
71
+ match event_type {
72
+ "ledgerClosed" => Ok ( Some ( TypedMessage :: LedgerClosed (
73
+ serde_json:: from_value ( value) ?,
74
+ ) ) ) ,
75
+ _ => Ok ( Some ( TypedMessage :: Other ( string) ) ) ,
76
+ }
77
+ } else {
78
+ Ok ( Some ( TypedMessage :: Other ( string) ) )
79
+ }
80
+ }
81
+ } else {
82
+ Ok ( None )
83
+ }
84
+ } )
85
+ . filter_map ( |res| future:: ready ( res. transpose ( ) ) ) ;
86
+
87
+ Ok ( Self {
88
+ sender,
89
+ messages : Box :: pin ( receiver) ,
90
+ requests : cloned_requests,
91
+ } )
33
92
}
34
93
35
94
pub async fn call < Req > ( & mut self , req : Req ) -> Result < ( ) >
@@ -43,14 +102,16 @@ impl Client {
43
102
// #TODO, this is temp code, add error-handling!
44
103
45
104
if let serde_json:: Value :: Object ( mut map) = msg {
46
- map. insert ( "id" . to_owned ( ) , serde_json:: Value :: String ( id) ) ;
105
+ map. insert ( "id" . to_owned ( ) , serde_json:: Value :: String ( id. clone ( ) ) ) ;
47
106
map. insert (
48
107
"command" . to_owned ( ) ,
49
108
serde_json:: Value :: String ( req. method ( ) ) ,
50
109
) ;
51
110
let msg = serde_json:: to_string ( & map) . unwrap ( ) ;
52
111
53
- self . stream . send ( Message :: Text ( msg. to_string ( ) ) ) . await ?;
112
+ self . sender . send ( Message :: Text ( msg. to_string ( ) ) ) . await ?;
113
+
114
+ self . requests . borrow_mut ( ) . insert ( id, req. method ( ) ) ;
54
115
}
55
116
56
117
Ok ( ( ) )
0 commit comments