@@ -8,12 +8,13 @@ use std::fmt::Debug;
8
8
use std:: future:: Future ;
9
9
10
10
/// An abstract HTTP client.
11
+ #[ cfg_attr( not( target_arch = "wasm32" ) , trait_variant:: make( Send ) ) ]
11
12
pub trait HttpClient {
12
13
/// Send an HTTP request and return the response.
13
14
fn send_http (
14
15
& self ,
15
16
request : Request < Vec < u8 > > ,
16
- ) -> impl Future < Output = core:: result:: Result < Response < Vec < u8 > > , Box < dyn std:: error:: Error + Send + Sync + ' static > > > + Send ;
17
+ ) -> impl Future < Output = core:: result:: Result < Response < Vec < u8 > > , Box < dyn std:: error:: Error + Send + Sync + ' static > > > ;
17
18
}
18
19
19
20
type XrpcResult < O , E > = core:: result:: Result < OutputDataOrBytes < O > , self :: Error < E > > ;
@@ -22,88 +23,111 @@ type XrpcResult<O, E> = core::result::Result<OutputDataOrBytes<O>, self::Error<E
22
23
///
23
24
/// [`send_xrpc()`](XrpcClient::send_xrpc) method has a default implementation,
24
25
/// which wraps the [`HttpClient::send_http()`]` method to handle input and output as an XRPC Request.
26
+ #[ cfg_attr( not( target_arch = "wasm32" ) , trait_variant:: make( Send ) ) ]
25
27
pub trait XrpcClient : HttpClient {
26
28
/// The base URI of the XRPC server.
27
29
fn base_uri ( & self ) -> String ;
28
30
/// Get the authentication token to use `Authorization` header.
29
31
#[ allow( unused_variables) ]
30
- fn authentication_token ( & self , is_refresh : bool ) -> impl Future < Output = Option < String > > + Send {
32
+ fn authentication_token ( & self , is_refresh : bool ) -> impl Future < Output = Option < String > > {
31
33
async { None }
32
34
}
33
35
/// Get the `atproto-proxy` header.
34
- fn atproto_proxy_header ( & self ) -> impl Future < Output = Option < String > > + Send {
36
+ fn atproto_proxy_header ( & self ) -> impl Future < Output = Option < String > > {
35
37
async { None }
36
38
}
37
39
/// Get the `atproto-accept-labelers` header.
38
- fn atproto_accept_labelers_header ( & self ) -> impl Future < Output = Option < Vec < String > > > + Send {
40
+ fn atproto_accept_labelers_header ( & self ) -> impl Future < Output = Option < Vec < String > > > {
39
41
async { None }
40
42
}
41
43
/// Send an XRPC request and return the response.
42
- fn send_xrpc < P , I , O , E > ( & self , request : & XrpcRequest < P , I > ) -> impl Future < Output = XrpcResult < O , E > > + Send
44
+ #[ cfg( not( target_arch = "wasm32" ) ) ]
45
+ fn send_xrpc < P , I , O , E > ( & self , request : & XrpcRequest < P , I > ) -> impl Future < Output = XrpcResult < O , E > >
43
46
where
44
47
P : Serialize + Send + Sync ,
45
48
I : Serialize + Send + Sync ,
46
49
O : DeserializeOwned + Send + Sync ,
47
50
E : DeserializeOwned + Send + Sync + Debug ,
51
+ // This code is duplicated because of this trait bound.
52
+ // `Self` has to be `Sync` for `Future` to be `Send`.
48
53
Self : Sync ,
49
54
{
50
- async {
51
- let mut uri = format ! ( "{}/xrpc/{}" , self . base_uri( ) , request. nsid) ;
52
- // Query parameters
53
- if let Some ( p) = & request. parameters {
54
- serde_html_form:: to_string ( p) . map ( |qs| {
55
- uri += "?" ;
56
- uri += & qs;
57
- } ) ?;
58
- } ;
59
- let mut builder = Request :: builder ( ) . method ( & request. method ) . uri ( & uri) ;
60
- // Headers
61
- if let Some ( encoding) = & request. encoding {
62
- builder = builder. header ( Header :: ContentType , encoding) ;
63
- }
64
- if let Some ( token) = self
65
- . authentication_token (
66
- request. method == Method :: POST && request. nsid == NSID_REFRESH_SESSION ,
67
- )
68
- . await
69
- {
70
- builder = builder. header ( Header :: Authorization , format ! ( "Bearer {}" , token) ) ;
71
- }
72
- if let Some ( proxy) = self . atproto_proxy_header ( ) . await {
73
- builder = builder. header ( Header :: AtprotoProxy , proxy) ;
74
- }
75
- if let Some ( accept_labelers) = self . atproto_accept_labelers_header ( ) . await {
76
- builder = builder. header ( Header :: AtprotoAcceptLabelers , accept_labelers. join ( ", " ) ) ;
77
- }
78
- // Body
79
- let body = if let Some ( input) = & request. input {
80
- match input {
81
- InputDataOrBytes :: Data ( data) => serde_json:: to_vec ( & data) ?,
82
- InputDataOrBytes :: Bytes ( bytes) => bytes. clone ( ) ,
83
- }
84
- } else {
85
- Vec :: new ( )
86
- } ;
87
- // Send
88
- let ( parts, body) =
89
- self . send_http ( builder. body ( body) ?) . await . map_err ( Error :: HttpClient ) ?. into_parts ( ) ;
90
- if parts. status . is_success ( ) {
91
- if parts
92
- . headers
93
- . get ( http:: header:: CONTENT_TYPE )
94
- . and_then ( |value| value. to_str ( ) . ok ( ) )
95
- . map_or ( false , |content_type| content_type. starts_with ( "application/json" ) )
96
- {
97
- Ok ( OutputDataOrBytes :: Data ( serde_json:: from_slice ( & body) ?) )
98
- } else {
99
- Ok ( OutputDataOrBytes :: Bytes ( body) )
100
- }
101
- } else {
102
- Err ( Error :: XrpcResponse ( XrpcError {
103
- status : parts. status ,
104
- error : serde_json:: from_slice :: < XrpcErrorKind < E > > ( & body) . ok ( ) ,
105
- } ) )
106
- }
107
- }
55
+ send_xrpc ( self , request)
56
+ }
57
+ #[ cfg( target_arch = "wasm32" ) ]
58
+ fn send_xrpc < P , I , O , E > ( & self , request : & XrpcRequest < P , I > ) -> impl Future < Output = XrpcResult < O , E > >
59
+ where
60
+ P : Serialize + Send + Sync ,
61
+ I : Serialize + Send + Sync ,
62
+ O : DeserializeOwned + Send + Sync ,
63
+ E : DeserializeOwned + Send + Sync + Debug ,
64
+ {
65
+ send_xrpc ( self , request)
108
66
}
109
67
}
68
+
69
+ #[ inline( always) ]
70
+ async fn send_xrpc < P , I , O , E , C : XrpcClient + ?Sized > ( client : & C , request : & XrpcRequest < P , I > ) -> XrpcResult < O , E >
71
+ where
72
+ P : Serialize + Send + Sync ,
73
+ I : Serialize + Send + Sync ,
74
+ O : DeserializeOwned + Send + Sync ,
75
+ E : DeserializeOwned + Send + Sync + Debug ,
76
+ {
77
+ let mut uri = format ! ( "{}/xrpc/{}" , client. base_uri( ) , request. nsid) ;
78
+ // Query parameters
79
+ if let Some ( p) = & request. parameters {
80
+ serde_html_form:: to_string ( p) . map ( |qs| {
81
+ uri += "?" ;
82
+ uri += & qs;
83
+ } ) ?;
84
+ } ;
85
+ let mut builder = Request :: builder ( ) . method ( & request. method ) . uri ( & uri) ;
86
+ // Headers
87
+ if let Some ( encoding) = & request. encoding {
88
+ builder = builder. header ( Header :: ContentType , encoding) ;
89
+ }
90
+ if let Some ( token) = client
91
+ . authentication_token (
92
+ request. method == Method :: POST && request. nsid == NSID_REFRESH_SESSION ,
93
+ )
94
+ . await
95
+ {
96
+ builder = builder. header ( Header :: Authorization , format ! ( "Bearer {}" , token) ) ;
97
+ }
98
+ if let Some ( proxy) = client. atproto_proxy_header ( ) . await {
99
+ builder = builder. header ( Header :: AtprotoProxy , proxy) ;
100
+ }
101
+ if let Some ( accept_labelers) = client. atproto_accept_labelers_header ( ) . await {
102
+ builder = builder. header ( Header :: AtprotoAcceptLabelers , accept_labelers. join ( ", " ) ) ;
103
+ }
104
+ // Body
105
+ let body = if let Some ( input) = & request. input {
106
+ match input {
107
+ InputDataOrBytes :: Data ( data) => serde_json:: to_vec ( & data) ?,
108
+ InputDataOrBytes :: Bytes ( bytes) => bytes. clone ( ) ,
109
+ }
110
+ } else {
111
+ Vec :: new ( )
112
+ } ;
113
+ // Send
114
+ let ( parts, body) =
115
+ client. send_http ( builder. body ( body) ?) . await . map_err ( Error :: HttpClient ) ?. into_parts ( ) ;
116
+ if parts. status . is_success ( ) {
117
+ if parts
118
+ . headers
119
+ . get ( http:: header:: CONTENT_TYPE )
120
+ . and_then ( |value| value. to_str ( ) . ok ( ) )
121
+ . map_or ( false , |content_type| content_type. starts_with ( "application/json" ) )
122
+ {
123
+ Ok ( OutputDataOrBytes :: Data ( serde_json:: from_slice ( & body) ?) )
124
+ } else {
125
+ Ok ( OutputDataOrBytes :: Bytes ( body) )
126
+ }
127
+ } else {
128
+ Err ( Error :: XrpcResponse ( XrpcError {
129
+ status : parts. status ,
130
+ error : serde_json:: from_slice :: < XrpcErrorKind < E > > ( & body) . ok ( ) ,
131
+ } ) )
132
+ }
133
+ }
0 commit comments