convex-rust 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +137 -2
  3. package/dist/cli/bin.d.ts +2 -0
  4. package/dist/cli/bin.js +200 -0
  5. package/dist/cli/build.d.ts +146 -0
  6. package/dist/cli/build.js +902 -0
  7. package/dist/cli/demangle.d.ts +7 -0
  8. package/dist/cli/demangle.js +328 -0
  9. package/dist/cli/diagnostics.d.ts +34 -0
  10. package/dist/cli/diagnostics.js +296 -0
  11. package/dist/cli/generate.d.ts +29 -0
  12. package/dist/cli/generate.js +82 -0
  13. package/dist/cli/manifest.d.ts +24 -0
  14. package/dist/cli/manifest.js +75 -0
  15. package/dist/cli/rustApi.d.ts +78 -0
  16. package/dist/cli/rustApi.js +609 -0
  17. package/dist/cli/scaffold.d.ts +22 -0
  18. package/dist/cli/scaffold.js +114 -0
  19. package/dist/cli/shim.d.ts +30 -0
  20. package/dist/cli/shim.js +140 -0
  21. package/dist/cli/size.d.ts +17 -0
  22. package/dist/cli/size.js +73 -0
  23. package/dist/cli/split.d.ts +20 -0
  24. package/dist/cli/split.js +140 -0
  25. package/dist/cli/symbols.d.ts +30 -0
  26. package/dist/cli/symbols.js +133 -0
  27. package/dist/cli/toolchain.d.ts +53 -0
  28. package/dist/cli/toolchain.js +167 -0
  29. package/dist/cli/validators.d.ts +6 -0
  30. package/dist/cli/validators.js +66 -0
  31. package/dist/cli/wasisdk.d.ts +82 -0
  32. package/dist/cli/wasisdk.js +220 -0
  33. package/dist/cli/wasmHints.d.ts +29 -0
  34. package/dist/cli/wasmHints.js +172 -0
  35. package/dist/runtime/base64.d.ts +4 -0
  36. package/dist/runtime/base64.js +77 -0
  37. package/dist/runtime/crypto.d.ts +7 -0
  38. package/dist/runtime/crypto.js +421 -0
  39. package/dist/runtime/fastjson.d.ts +3 -0
  40. package/dist/runtime/fastjson.js +170 -0
  41. package/dist/runtime/host.d.ts +73 -0
  42. package/dist/runtime/host.js +1230 -0
  43. package/dist/runtime/http.d.ts +40 -0
  44. package/dist/runtime/http.js +157 -0
  45. package/dist/runtime/index.d.ts +2 -0
  46. package/dist/runtime/index.js +1 -0
  47. package/dist/runtime/intl.d.ts +5 -0
  48. package/dist/runtime/intl.js +69 -0
  49. package/dist/runtime/tmpfs.d.ts +129 -0
  50. package/dist/runtime/tmpfs.js +624 -0
  51. package/dist/runtime/wasi.d.ts +33 -0
  52. package/dist/runtime/wasi.js +216 -0
  53. package/package.json +66 -4
  54. package/rust/convex_fn/Cargo.toml +32 -0
  55. package/rust/convex_fn/LICENSE +201 -0
  56. package/rust/convex_fn/build.rs +3 -0
  57. package/rust/convex_fn/src/args.rs +416 -0
  58. package/rust/convex_fn/src/crypto.rs +237 -0
  59. package/rust/convex_fn/src/ctx.rs +1044 -0
  60. package/rust/convex_fn/src/dns.rs +356 -0
  61. package/rust/convex_fn/src/error.rs +179 -0
  62. package/rust/convex_fn/src/executor.rs +577 -0
  63. package/rust/convex_fn/src/fanout.rs +106 -0
  64. package/rust/convex_fn/src/fs.rs +19 -0
  65. package/rust/convex_fn/src/function_ref.rs +219 -0
  66. package/rust/convex_fn/src/http.rs +441 -0
  67. package/rust/convex_fn/src/intl.rs +75 -0
  68. package/rust/convex_fn/src/lib.rs +242 -0
  69. package/rust/convex_fn/src/logging.rs +222 -0
  70. package/rust/convex_fn/src/native.rs +360 -0
  71. package/rust/convex_fn/src/net.rs +122 -0
  72. package/rust/convex_fn/src/sys.rs +178 -0
  73. package/rust/convex_fn/src/values.rs +121 -0
  74. package/rust/convex_fn/src/wire.rs +579 -0
  75. package/rust/convex_fn_macros/Cargo.toml +13 -0
  76. package/rust/convex_fn_macros/LICENSE +201 -0
  77. package/rust/convex_fn_macros/src/derive.rs +632 -0
  78. package/rust/convex_fn_macros/src/lib.rs +338 -0
  79. package/rust/convex_reqwest/Cargo.toml +69 -0
  80. package/rust/convex_reqwest/LICENSE +201 -0
  81. package/rust/convex_reqwest/src/blocking.rs +287 -0
  82. package/rust/convex_reqwest/src/body.rs +152 -0
  83. package/rust/convex_reqwest/src/client.rs +359 -0
  84. package/rust/convex_reqwest/src/config_types.rs +150 -0
  85. package/rust/convex_reqwest/src/error.rs +241 -0
  86. package/rust/convex_reqwest/src/into_url.rs +57 -0
  87. package/rust/convex_reqwest/src/lib.rs +42 -0
  88. package/rust/convex_reqwest/src/multipart.rs +140 -0
  89. package/rust/convex_reqwest/src/request.rs +358 -0
  90. package/rust/convex_reqwest/src/response.rs +310 -0
@@ -0,0 +1,359 @@
1
+ use std::{fmt, future::Future, net::SocketAddr, sync::Arc, time::Duration};
2
+
3
+ use convex_fn::{select, Ctx, Either, FetchRequest, Redirect};
4
+ use http::{
5
+ header::{HeaderMap, HeaderName, HeaderValue, USER_AGENT},
6
+ Method, StatusCode,
7
+ };
8
+ use url::Url;
9
+
10
+ use crate::{
11
+ body::Body,
12
+ error::{Error, Kind, Result},
13
+ into_url::IntoUrl,
14
+ redirect, tls, Proxy, Request, RequestBuilder, Response,
15
+ };
16
+
17
+ /// An asynchronous `Client` to make Requests with. Every request becomes a
18
+ /// Convex `Fetch` task, so egress policy, limits, metering and logs are the
19
+ /// host's, exactly as for `fetch` in a JS action.
20
+ #[derive(Clone)]
21
+ pub struct Client {
22
+ inner: Arc<Config>,
23
+ }
24
+
25
+ #[derive(Default)]
26
+ struct Config {
27
+ headers: HeaderMap,
28
+ timeout: Option<Duration>,
29
+ https_only: bool,
30
+ no_redirect: bool,
31
+ }
32
+
33
+ impl Default for Client {
34
+ fn default() -> Self {
35
+ Self::new()
36
+ }
37
+ }
38
+
39
+ impl Client {
40
+ /// Constructs a new `Client`.
41
+ pub fn new() -> Client {
42
+ Client {
43
+ inner: Arc::new(Config::default()),
44
+ }
45
+ }
46
+
47
+ /// Creates a `ClientBuilder` to configure a `Client`.
48
+ pub fn builder() -> ClientBuilder {
49
+ ClientBuilder::new()
50
+ }
51
+
52
+ pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
53
+ self.request(Method::GET, url)
54
+ }
55
+ pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
56
+ self.request(Method::POST, url)
57
+ }
58
+ pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
59
+ self.request(Method::PUT, url)
60
+ }
61
+ pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
62
+ self.request(Method::PATCH, url)
63
+ }
64
+ pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
65
+ self.request(Method::DELETE, url)
66
+ }
67
+ pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
68
+ self.request(Method::HEAD, url)
69
+ }
70
+
71
+ /// Start building a `Request` with the `Method` and `Url`.
72
+ pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
73
+ let req = url.into_url().map(|url| Request::new(method, url));
74
+ RequestBuilder::new(self.clone(), req)
75
+ }
76
+
77
+ /// Executes a `Request`.
78
+ pub fn execute(&self, request: Request) -> impl Future<Output = Result<Response>> {
79
+ let this = self.clone();
80
+ async move { this.execute_inner(request).await }
81
+ }
82
+
83
+ async fn execute_inner(&self, request: Request) -> Result<Response> {
84
+ let (method, url, mut headers, body, timeout) = request.into_parts();
85
+ if self.inner.https_only && url.scheme() != "https" {
86
+ return Err(Error::builder_msg("URL scheme is not allowed").with_url(url));
87
+ }
88
+ let timeout = timeout.or(self.inner.timeout);
89
+ for (k, v) in &self.inner.headers {
90
+ if !headers.contains_key(k) {
91
+ headers.append(k.clone(), v.clone());
92
+ }
93
+ }
94
+ // An in-memory body is sent as is; a streaming one is uploaded chunk by chunk
95
+ // (`Ctx::fetch_upload`), so it never has to fit in memory.
96
+ let mut upload = None;
97
+ let body = match body.map(Body::into_source) {
98
+ Some(Ok(b)) => Some(b),
99
+ Some(Err(src)) => {
100
+ upload = Some(src);
101
+ None
102
+ }
103
+ None => None,
104
+ };
105
+ let mut fr = FetchRequest {
106
+ url: url.to_string(),
107
+ method: method.as_str().to_string(),
108
+ headers: headers
109
+ .iter()
110
+ .map(|(k, v)| (k.as_str().to_string(), latin1(v.as_bytes())))
111
+ .collect(),
112
+ body: body.map(|b| b.to_vec()),
113
+ redirect: if self.inner.no_redirect {
114
+ Redirect::Manual
115
+ } else {
116
+ Redirect::Follow
117
+ },
118
+ };
119
+ if matches!(method, Method::GET | Method::HEAD)
120
+ && fr.body.as_ref().is_some_and(Vec::is_empty)
121
+ {
122
+ fr.body = None;
123
+ }
124
+
125
+ let ctx = Ctx::default();
126
+ // Dropping `send` (the timeout below, or the caller dropping the request future)
127
+ // aborts the host fetch.
128
+ let send = async {
129
+ match upload {
130
+ Some(src) => ctx.fetch_stream_upload(fr, src).await,
131
+ None => ctx.fetch_stream(fr).await,
132
+ }
133
+ };
134
+ let stream = match timeout {
135
+ // The host `sleep` task, not a tokio timer: inside `block_on_direct` (a blocking client
136
+ // in an action) no tokio timer is ever driven. Dropping `send` cancels the fetch.
137
+ Some(t) => match select(send, ctx.sleep(millis(t))).await {
138
+ Either::Left(r) => r,
139
+ Either::Right(_) => return Err(Error::timeout(Kind::Request).with_url(url)),
140
+ },
141
+ None => send.await,
142
+ }
143
+ .map_err(|e| {
144
+ Error::request(crate::error::FetchFailed(e.to_string())).with_url(url.clone())
145
+ })?;
146
+
147
+ let status = StatusCode::from_u16(stream.status)
148
+ .map_err(|e| Error::request(e).with_url(url.clone()))?;
149
+ let mut resp_headers = HeaderMap::new();
150
+ for (k, v) in &stream.headers {
151
+ if let (Ok(k), Ok(v)) = (
152
+ HeaderName::from_bytes(k.as_bytes()),
153
+ header_value(v),
154
+ ) {
155
+ resp_headers.append(k, v);
156
+ }
157
+ }
158
+ let final_url = Url::parse(&stream.url).unwrap_or(url);
159
+ Ok(Response::from_fetch(
160
+ stream,
161
+ status,
162
+ resp_headers,
163
+ final_url,
164
+ timeout,
165
+ ))
166
+ }
167
+ }
168
+
169
+ impl fmt::Debug for Client {
170
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171
+ f.debug_struct("Client").finish()
172
+ }
173
+ }
174
+
175
+ /// A `ClientBuilder` can be used to create a `Client` with custom
176
+ /// configuration. Options with no equivalent for host-side fetch (TLS,
177
+ /// proxies, pooling, redirects, ...) are accepted and ignored.
178
+ #[must_use]
179
+ pub struct ClientBuilder {
180
+ config: Config,
181
+ error: Option<Error>,
182
+ }
183
+
184
+ impl Default for ClientBuilder {
185
+ fn default() -> Self {
186
+ Self::new()
187
+ }
188
+ }
189
+
190
+ macro_rules! ignored {
191
+ ($($(#[$m:meta])* $name:ident($($arg:ident: $ty:ty),*);)*) => {
192
+ $(
193
+ $(#[$m])*
194
+ pub fn $name(self $(, $arg: $ty)*) -> ClientBuilder {
195
+ $(let _ = $arg;)*
196
+ self
197
+ }
198
+ )*
199
+ };
200
+ }
201
+
202
+ impl ClientBuilder {
203
+ pub fn new() -> ClientBuilder {
204
+ ClientBuilder {
205
+ config: Config::default(),
206
+ error: None,
207
+ }
208
+ }
209
+
210
+ /// Returns a `Client` that uses this `ClientBuilder` configuration.
211
+ pub fn build(self) -> Result<Client> {
212
+ match self.error {
213
+ Some(e) => Err(e),
214
+ None => Ok(Client {
215
+ inner: Arc::new(self.config),
216
+ }),
217
+ }
218
+ }
219
+
220
+ /// Sets the default headers for every request.
221
+ pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
222
+ for (k, v) in headers.iter() {
223
+ self.config.headers.insert(k.clone(), v.clone());
224
+ }
225
+ self
226
+ }
227
+
228
+ /// Sets the `User-Agent` header to be used by this client.
229
+ pub fn user_agent<V>(mut self, value: V) -> ClientBuilder
230
+ where
231
+ HeaderValue: TryFrom<V>,
232
+ <HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
233
+ {
234
+ match HeaderValue::try_from(value) {
235
+ Ok(v) => {
236
+ self.config.headers.insert(USER_AGENT, v);
237
+ }
238
+ Err(e) => self.error = Some(Error::builder(e.into())),
239
+ }
240
+ self
241
+ }
242
+
243
+ /// Enables a total request timeout.
244
+ pub fn timeout(mut self, timeout: Duration) -> ClientBuilder {
245
+ self.config.timeout = Some(timeout);
246
+ self
247
+ }
248
+
249
+ /// Only use HTTPS URLs.
250
+ pub fn https_only(mut self, enabled: bool) -> ClientBuilder {
251
+ self.config.https_only = enabled;
252
+ self
253
+ }
254
+
255
+ /// Sets the redirect policy. `Policy::none()` returns 3xx responses as
256
+ /// is; any other policy follows redirects (the host's limit applies).
257
+ pub fn redirect(mut self, policy: redirect::Policy) -> ClientBuilder {
258
+ self.config.no_redirect = policy.is_none();
259
+ self
260
+ }
261
+
262
+ /// Add a proxy (accepted, ignored).
263
+ pub fn proxy(self, proxy: Proxy) -> ClientBuilder {
264
+ let _ = proxy;
265
+ self
266
+ }
267
+
268
+ /// Add a root certificate (accepted, ignored).
269
+ pub fn add_root_certificate(self, cert: tls::Certificate) -> ClientBuilder {
270
+ let _ = cert;
271
+ self
272
+ }
273
+
274
+ /// Sets the identity to be used for client certificate authentication (ignored).
275
+ pub fn identity(self, identity: tls::Identity) -> ClientBuilder {
276
+ let _ = identity;
277
+ self
278
+ }
279
+
280
+ ignored! {
281
+ connect_timeout(timeout: Duration);
282
+ read_timeout(timeout: Duration);
283
+ no_proxy();
284
+ pool_idle_timeout(val: impl Into<Option<Duration>>);
285
+ pool_max_idle_per_host(max: usize);
286
+ http1_only();
287
+ http1_title_case_headers();
288
+ http2_prior_knowledge();
289
+ http2_adaptive_window(enabled: bool);
290
+ http2_keep_alive_interval(interval: impl Into<Option<Duration>>);
291
+ http2_keep_alive_timeout(timeout: Duration);
292
+ http2_keep_alive_while_idle(enabled: bool);
293
+ tcp_nodelay(enabled: bool);
294
+ tcp_keepalive(val: impl Into<Option<Duration>>);
295
+ local_address(addr: impl Into<Option<std::net::IpAddr>>);
296
+ referer(enable: bool);
297
+ gzip(enable: bool);
298
+ brotli(enable: bool);
299
+ zstd(enable: bool);
300
+ deflate(enable: bool);
301
+ no_gzip();
302
+ no_brotli();
303
+ no_zstd();
304
+ no_deflate();
305
+ cookie_store(enable: bool);
306
+ hickory_dns(enable: bool);
307
+ trust_dns(enable: bool);
308
+ no_hickory_dns();
309
+ no_trust_dns();
310
+ danger_accept_invalid_certs(accept: bool);
311
+ danger_accept_invalid_hostnames(accept: bool);
312
+ tls_built_in_root_certs(enabled: bool);
313
+ tls_built_in_webpki_certs(enabled: bool);
314
+ tls_built_in_native_certs(enabled: bool);
315
+ tls_sni(enabled: bool);
316
+ min_tls_version(version: tls::Version);
317
+ max_tls_version(version: tls::Version);
318
+ use_native_tls();
319
+ use_rustls_tls();
320
+ tls_info(enabled: bool);
321
+ connection_verbose(enabled: bool);
322
+ }
323
+
324
+ /// Override DNS resolution for a domain (ignored).
325
+ pub fn resolve(self, domain: &str, addr: SocketAddr) -> ClientBuilder {
326
+ let _ = (domain, addr);
327
+ self
328
+ }
329
+ }
330
+
331
+ impl fmt::Debug for ClientBuilder {
332
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
333
+ f.debug_struct("ClientBuilder").finish()
334
+ }
335
+ }
336
+
337
+ /// A timeout as whole host-sleep milliseconds (rounded up, at least 1).
338
+ pub(crate) fn millis(d: Duration) -> u64 {
339
+ u64::try_from(d.as_nanos().div_ceil(1_000_000))
340
+ .unwrap_or(u64::MAX)
341
+ .max(1)
342
+ }
343
+
344
+ /// A header value's bytes as the ByteString `fetch` takes (one char per byte), so values that
345
+ /// are not UTF-8 survive the round trip instead of being dropped.
346
+ fn latin1(b: &[u8]) -> String {
347
+ b.iter().map(|&c| c as char).collect()
348
+ }
349
+
350
+ /// The inverse of [`latin1`] for response headers (fetch hands out ByteStrings); a value with
351
+ /// chars above U+00FF is taken as UTF-8.
352
+ fn header_value(s: &str) -> std::result::Result<HeaderValue, http::header::InvalidHeaderValue> {
353
+ if s.chars().all(|c| (c as u32) < 256) {
354
+ let bytes: Vec<u8> = s.chars().map(|c| c as u8).collect();
355
+ HeaderValue::from_bytes(&bytes)
356
+ } else {
357
+ HeaderValue::from_str(s)
358
+ }
359
+ }
@@ -0,0 +1,150 @@
1
+ //! Types for options that the Convex host owns (redirects, proxies, TLS).
2
+ //! They exist so vendor crates compile; they have no effect.
3
+
4
+ pub mod redirect {
5
+ use http::StatusCode;
6
+ use url::Url;
7
+
8
+ /// A type that controls the policy on how to handle the following of redirects.
9
+ pub struct Policy {
10
+ _max: Option<usize>,
11
+ }
12
+
13
+ impl Policy {
14
+ pub fn limited(max: usize) -> Self {
15
+ Policy { _max: Some(max) }
16
+ }
17
+ pub fn none() -> Self {
18
+ Policy { _max: Some(0) }
19
+ }
20
+ pub fn custom<T>(policy: T) -> Self
21
+ where
22
+ T: Fn(Attempt) -> Action + Send + Sync + 'static,
23
+ {
24
+ let _ = policy;
25
+ Policy { _max: None }
26
+ }
27
+ /// `Policy::none()`: the host returns 3xx responses as is. Any other
28
+ /// policy follows redirects (the host's limit is 20 hops).
29
+ pub(crate) fn is_none(&self) -> bool {
30
+ self._max == Some(0)
31
+ }
32
+ pub fn redirect(&self, attempt: Attempt) -> Action {
33
+ match self._max {
34
+ Some(m) if attempt.previous.len() >= m => attempt.stop(),
35
+ _ => attempt.follow(),
36
+ }
37
+ }
38
+ }
39
+
40
+ impl Default for Policy {
41
+ fn default() -> Policy {
42
+ Policy::limited(10)
43
+ }
44
+ }
45
+
46
+ /// A type that holds information on the next request and previous requests in a redirect chain.
47
+ #[derive(Debug)]
48
+ pub struct Attempt<'a> {
49
+ status: StatusCode,
50
+ next: &'a Url,
51
+ previous: &'a [Url],
52
+ }
53
+
54
+ /// An action to perform when a redirect status code is found.
55
+ #[derive(Debug)]
56
+ pub struct Action {
57
+ _follow: bool,
58
+ }
59
+
60
+ impl<'a> Attempt<'a> {
61
+ pub fn status(&self) -> StatusCode {
62
+ self.status
63
+ }
64
+ pub fn url(&self) -> &Url {
65
+ self.next
66
+ }
67
+ pub fn previous(&self) -> &[Url] {
68
+ self.previous
69
+ }
70
+ pub fn follow(self) -> Action {
71
+ Action { _follow: true }
72
+ }
73
+ pub fn stop(self) -> Action {
74
+ Action { _follow: false }
75
+ }
76
+ pub fn error<E: Into<Box<dyn std::error::Error + Send + Sync>>>(self, error: E) -> Action {
77
+ let _ = error.into();
78
+ Action { _follow: false }
79
+ }
80
+ }
81
+ }
82
+
83
+ use crate::{error::Result, into_url::IntoUrl};
84
+
85
+ /// Configuration of a proxy (accepted, ignored).
86
+ #[derive(Debug, Clone)]
87
+ pub struct Proxy {
88
+ _p: (),
89
+ }
90
+
91
+ impl Proxy {
92
+ pub fn all<U: IntoUrl>(proxy_scheme: U) -> Result<Proxy> {
93
+ proxy_scheme.into_url().map(|_| Proxy { _p: () })
94
+ }
95
+ pub fn http<U: IntoUrl>(proxy_scheme: U) -> Result<Proxy> {
96
+ Self::all(proxy_scheme)
97
+ }
98
+ pub fn https<U: IntoUrl>(proxy_scheme: U) -> Result<Proxy> {
99
+ Self::all(proxy_scheme)
100
+ }
101
+ pub fn basic_auth(self, username: &str, password: &str) -> Proxy {
102
+ let _ = (username, password);
103
+ self
104
+ }
105
+ }
106
+
107
+ pub mod tls {
108
+ use crate::error::Result;
109
+
110
+ /// A TLS protocol version.
111
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
112
+ pub struct Version(u8);
113
+
114
+ impl Version {
115
+ pub const TLS_1_0: Version = Version(0);
116
+ pub const TLS_1_1: Version = Version(1);
117
+ pub const TLS_1_2: Version = Version(2);
118
+ pub const TLS_1_3: Version = Version(3);
119
+ }
120
+
121
+ /// A root certificate (accepted, ignored).
122
+ #[derive(Debug, Clone)]
123
+ pub struct Certificate {
124
+ _p: (),
125
+ }
126
+
127
+ impl Certificate {
128
+ pub fn from_der(der: &[u8]) -> Result<Certificate> {
129
+ let _ = der;
130
+ Ok(Certificate { _p: () })
131
+ }
132
+ pub fn from_pem(pem: &[u8]) -> Result<Certificate> {
133
+ let _ = pem;
134
+ Ok(Certificate { _p: () })
135
+ }
136
+ }
137
+
138
+ /// A client identity (accepted, ignored).
139
+ #[derive(Debug, Clone)]
140
+ pub struct Identity {
141
+ _p: (),
142
+ }
143
+
144
+ impl Identity {
145
+ pub fn from_pem(buf: &[u8]) -> Result<Identity> {
146
+ let _ = buf;
147
+ Ok(Identity { _p: () })
148
+ }
149
+ }
150
+ }