tangled
alpha
login
or
join now
nonbinary.computer
/
jacquard
80
fork
atom
A better Rust ATProto crate
80
fork
atom
overview
issues
9
pulls
pipelines
ported minimal changes from trait branch
Orual
4 months ago
61b91cf5
b5cc9b35
+251
-6
2 changed files
expand all
collapse all
unified
split
crates
jacquard
src
client.rs
jacquard-common
src
types
cid.rs
+3
-6
crates/jacquard-common/src/types/cid.rs
···
1
1
-
use crate::{CowStr, IntoStatic};
1
1
+
use crate::{CowStr, IntoStatic, cowstr::ToCowStr};
2
2
pub use cid::Cid as IpldCid;
3
3
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Visitor};
4
4
use smol_str::ToSmolStr;
···
379
379
where
380
380
E: serde::de::Error,
381
381
{
382
382
-
if let Ok(cid) = IpldCid::try_from(v.as_bytes()) {
383
383
-
Ok(CidLink(Cid::ipld(cid)))
384
384
-
} else {
385
385
-
Err(E::custom("invalid CID string"))
386
386
-
}
382
382
+
// TODO: currently overly permissive, should fix
383
383
+
Ok(CidLink::cow_str(v.to_cowstr()).into_static())
387
384
}
388
385
389
386
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
+248
crates/jacquard/src/client.rs
···
47
47
use jacquard_common::types::string::AtUri;
48
48
#[cfg(feature = "api")]
49
49
use jacquard_common::types::uri::RecordUri;
50
50
+
#[cfg(not(target_arch = "wasm32"))]
51
51
+
use jacquard_common::xrpc::XrpcResponse;
50
52
use jacquard_common::xrpc::{
51
53
CallOptions, Response, XrpcClient, XrpcError, XrpcExt, XrpcRequest, XrpcResp,
52
54
};
···
58
60
use jacquard_identity::resolver::{
59
61
DidDocResponse, IdentityError, IdentityResolver, ResolverOptions,
60
62
};
63
63
+
use jacquard_identity::{JacquardResolver, slingshot_resolver_default};
61
64
use jacquard_oauth::authstore::ClientAuthStore;
62
65
use jacquard_oauth::client::OAuthSession;
63
66
use jacquard_oauth::dpop::DpopExt;
···
66
69
#[cfg(feature = "api")]
67
70
use std::marker::Send;
68
71
use std::option::Option;
72
72
+
use std::sync::Arc;
69
73
pub use token::FileAuthStore;
74
74
+
use tokio::sync::RwLock;
75
75
+
use url::Url;
70
76
71
77
/// Identifies the active authentication mode for an agent/session.
72
78
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
···
142
148
impl Default for BasicClient {
143
149
fn default() -> Self {
144
150
Self::unauthenticated()
151
151
+
}
152
152
+
}
153
153
+
pub struct UnauthenticatedSession<T> {
154
154
+
resolver: Arc<T>,
155
155
+
endpoint: Arc<RwLock<Option<Url>>>,
156
156
+
options: Arc<RwLock<CallOptions<'static>>>,
157
157
+
}
158
158
+
159
159
+
impl Default for UnauthenticatedSession<JacquardResolver> {
160
160
+
fn default() -> Self {
161
161
+
Self::new_public()
162
162
+
}
163
163
+
}
164
164
+
165
165
+
impl UnauthenticatedSession<JacquardResolver> {
166
166
+
pub fn new_public() -> Self {
167
167
+
let resolver = Arc::new(JacquardResolver::default());
168
168
+
let endpoint = Arc::new(RwLock::new(None));
169
169
+
let options = Arc::new(RwLock::new(CallOptions::default()));
170
170
+
Self {
171
171
+
resolver,
172
172
+
endpoint,
173
173
+
options,
174
174
+
}
175
175
+
}
176
176
+
177
177
+
pub fn new_slingshot() -> Self {
178
178
+
let resolver = Arc::new(slingshot_resolver_default());
179
179
+
let endpoint = Arc::new(RwLock::new(None));
180
180
+
let options = Arc::new(RwLock::new(CallOptions::default()));
181
181
+
Self {
182
182
+
resolver,
183
183
+
endpoint,
184
184
+
options,
185
185
+
}
186
186
+
}
187
187
+
}
188
188
+
189
189
+
impl<T: HttpClient + Sync> HttpClient for UnauthenticatedSession<T> {
190
190
+
type Error = T::Error;
191
191
+
192
192
+
#[cfg(not(target_arch = "wasm32"))]
193
193
+
fn send_http(
194
194
+
&self,
195
195
+
request: http::Request<Vec<u8>>,
196
196
+
) -> impl Future<Output = core::result::Result<http::Response<Vec<u8>>, T::Error>> + Send {
197
197
+
self.resolver.send_http(request)
198
198
+
}
199
199
+
200
200
+
#[cfg(target_arch = "wasm32")]
201
201
+
fn send_http(
202
202
+
&self,
203
203
+
request: http::Request<Vec<u8>>,
204
204
+
) -> impl Future<Output = core::result::Result<http::Response<Vec<u8>>, T::Error>> {
205
205
+
self.resolver.send_http(request)
206
206
+
}
207
207
+
}
208
208
+
209
209
+
impl<T: HttpClient> XrpcClient for UnauthenticatedSession<T>
210
210
+
where
211
211
+
T: Sync + Send,
212
212
+
{
213
213
+
#[doc = " Get the base URI for the client."]
214
214
+
fn base_uri(&self) -> impl Future<Output = Url> + Send {
215
215
+
async move {
216
216
+
self.endpoint.read().await.clone().unwrap_or(
217
217
+
Url::parse("https://public.bsky.app").expect("public appview should be valid url"),
218
218
+
)
219
219
+
}
220
220
+
}
221
221
+
222
222
+
#[doc = " Send an XRPC request and parse the response"]
223
223
+
#[cfg(not(target_arch = "wasm32"))]
224
224
+
fn send<R>(&self, request: R) -> impl Future<Output = XrpcResult<XrpcResponse<R>>> + Send
225
225
+
where
226
226
+
R: XrpcRequest + Send + Sync,
227
227
+
<R as XrpcRequest>::Response: Send + Sync,
228
228
+
Self: Sync,
229
229
+
{
230
230
+
async move {
231
231
+
let opts = self.options.read().await.clone();
232
232
+
self.send_with_opts(request, opts).await
233
233
+
}
234
234
+
}
235
235
+
236
236
+
#[doc = " Send an XRPC request and parse the response"]
237
237
+
#[cfg(not(target_arch = "wasm32"))]
238
238
+
fn send_with_opts<R>(
239
239
+
&self,
240
240
+
request: R,
241
241
+
opts: CallOptions<'_>,
242
242
+
) -> impl Future<Output = XrpcResult<XrpcResponse<R>>> + Send
243
243
+
where
244
244
+
R: XrpcRequest + Send + Sync,
245
245
+
<R as XrpcRequest>::Response: Send + Sync,
246
246
+
Self: Sync,
247
247
+
{
248
248
+
async move {
249
249
+
let base_uri = self.base_uri().await;
250
250
+
self.resolver
251
251
+
.xrpc(base_uri.clone())
252
252
+
.with_options(opts.clone())
253
253
+
.send(&request)
254
254
+
.await
255
255
+
}
256
256
+
}
257
257
+
258
258
+
#[doc = " Send an XRPC request and parse the response"]
259
259
+
#[cfg(target_arch = "wasm32")]
260
260
+
fn send<R>(&self, request: R) -> impl Future<Output = XrpcResult<XrpcResponse<R>>>
261
261
+
where
262
262
+
R: XrpcRequest + Send + Sync,
263
263
+
<R as XrpcRequest>::Response: Send + Sync,
264
264
+
{
265
265
+
async move {
266
266
+
let opts = self.options.read().await.clone();
267
267
+
self.send_with_opts(request, opts).await
268
268
+
}
269
269
+
}
270
270
+
271
271
+
#[doc = " Send an XRPC request and parse the response"]
272
272
+
#[cfg(target_arch = "wasm32")]
273
273
+
fn send_with_opts<R>(
274
274
+
&self,
275
275
+
request: R,
276
276
+
opts: CallOptions<'_>,
277
277
+
) -> impl Future<Output = XrpcResult<XrpcResponse<R>>>
278
278
+
where
279
279
+
R: XrpcRequest + Send + Sync,
280
280
+
<R as XrpcRequest>::Response: Send + Sync,
281
281
+
{
282
282
+
async move {
283
283
+
let base_uri = self.base_uri().await;
284
284
+
self.resolver
285
285
+
.xrpc(base_uri.clone())
286
286
+
.with_options(opts.clone())
287
287
+
.send(&request)
288
288
+
.await
289
289
+
}
290
290
+
}
291
291
+
292
292
+
#[doc = " Set the base URI for the client."]
293
293
+
fn set_base_uri(&self, url: Url) -> impl Future<Output = ()> + Send {
294
294
+
async move {
295
295
+
let mut guard = self.endpoint.write().await;
296
296
+
*guard = Some(url);
297
297
+
}
298
298
+
}
299
299
+
300
300
+
#[doc = " Get the call options for the client."]
301
301
+
fn opts(&self) -> impl Future<Output = CallOptions<'_>> + Send {
302
302
+
async move { self.options.read().await.clone() }
303
303
+
}
304
304
+
305
305
+
#[doc = " Set the call options for the client."]
306
306
+
fn set_opts(&self, opts: CallOptions<'_>) -> impl Future<Output = ()> + Send {
307
307
+
async move {
308
308
+
*self.options.write().await = opts.into_static();
309
309
+
}
310
310
+
}
311
311
+
}
312
312
+
313
313
+
impl<T: IdentityResolver + HttpClient> AgentSession for UnauthenticatedSession<T>
314
314
+
where
315
315
+
T: Sync + Send,
316
316
+
{
317
317
+
fn session_kind(&self) -> AgentKind {
318
318
+
AgentKind::AppPassword
319
319
+
}
320
320
+
321
321
+
fn session_info(
322
322
+
&self,
323
323
+
) -> impl Future<Output = Option<(Did<'static>, Option<CowStr<'static>>)>> {
324
324
+
async { None } // no session
325
325
+
}
326
326
+
327
327
+
fn endpoint(&self) -> impl Future<Output = Url> {
328
328
+
async { self.base_uri().await }
329
329
+
}
330
330
+
331
331
+
#[doc = " Override per-session call options."]
332
332
+
fn set_options<'a>(&'a self, opts: CallOptions<'a>) -> impl Future<Output = ()> + Send {
333
333
+
async move {
334
334
+
*self.options.write().await = opts.into_static();
335
335
+
}
336
336
+
}
337
337
+
338
338
+
#[doc = " Refresh the session and return a fresh AuthorizationToken."]
339
339
+
fn refresh(&self) -> impl Future<Output = ClientResult<AuthorizationToken<'static>>> + Send {
340
340
+
async {
341
341
+
Err(ClientError::auth(
342
342
+
jacquard_common::error::AuthError::NotAuthenticated,
343
343
+
))
344
344
+
}
345
345
+
}
346
346
+
}
347
347
+
348
348
+
impl<T: IdentityResolver + Sync> IdentityResolver for UnauthenticatedSession<T> {
349
349
+
#[doc = " Access options for validation decisions in default methods"]
350
350
+
fn options(&self) -> &ResolverOptions {
351
351
+
self.resolver.options()
352
352
+
}
353
353
+
354
354
+
#[doc = " Resolve handle"]
355
355
+
#[cfg(not(target_arch = "wasm32"))]
356
356
+
fn resolve_handle(
357
357
+
&self,
358
358
+
handle: &Handle<'_>,
359
359
+
) -> impl Future<Output = std::result::Result<Did<'static>, IdentityError>> + Send
360
360
+
where
361
361
+
Self: Sync,
362
362
+
{
363
363
+
self.resolver.resolve_handle(handle)
364
364
+
}
365
365
+
366
366
+
#[doc = " Resolve DID document"]
367
367
+
#[cfg(not(target_arch = "wasm32"))]
368
368
+
fn resolve_did_doc(
369
369
+
&self,
370
370
+
did: &Did<'_>,
371
371
+
) -> impl Future<Output = std::result::Result<DidDocResponse, IdentityError>> + Send
372
372
+
where
373
373
+
Self: Sync,
374
374
+
{
375
375
+
self.resolver.resolve_did_doc(did)
376
376
+
}
377
377
+
#[doc = " Resolve handle"]
378
378
+
#[cfg(target_arch = "wasm32")]
379
379
+
fn resolve_handle(
380
380
+
&self,
381
381
+
handle: &Handle<'_>,
382
382
+
) -> impl Future<Output = std::result::Result<Did<'static>, IdentityError>> {
383
383
+
self.resolver.resolve_handle(handle)
384
384
+
}
385
385
+
386
386
+
#[doc = " Resolve DID document"]
387
387
+
#[cfg(target_arch = "wasm32")]
388
388
+
fn resolve_did_doc(
389
389
+
&self,
390
390
+
did: &Did<'_>,
391
391
+
) -> impl Future<Output = std::result::Result<DidDocResponse, IdentityError>> {
392
392
+
self.resolver.resolve_did_doc(did)
145
393
}
146
394
}
147
395