A better Rust ATProto crate

fixed bug in plc.directory did doc resolution

Orual 2112129b c464e6a5

Changed files
+49 -26
crates
jacquard
jacquard-identity
+5 -1
crates/jacquard-identity/src/lib.rs
···
}
DidStep::PlcHttp if s.starts_with("did:plc:") => {
let url = match &self.opts.plc_source {
-
PlcSource::PlcDirectory { base } => base.join(did.as_str())?,
+
PlcSource::PlcDirectory { base } => {
+
// this is odd, the join screws up with the plc directory but NOT slingshot
+
Url::parse(&format!("{}{}", base, did.as_str())).expect("Invalid URL")
+
}
PlcSource::Slingshot { base } => base.join(did.as_str())?,
};
+
println!("Fetching DID document from {}", url);
if let Ok((buf, status)) = self.get_json_bytes(url).await {
return Ok(DidDocResponse {
buffer: buf,
+28 -7
crates/jacquard-identity/src/resolver.rs
···
#[allow(missing_docs)]
pub enum IdentityError {
#[error("unsupported DID method: {0}")]
-
#[diagnostic(code(jacquard_identity::unsupported_did_method), help("supported DID methods: did:web, did:plc"))]
+
#[diagnostic(
+
code(jacquard_identity::unsupported_did_method),
+
help("supported DID methods: did:web, did:plc")
+
)]
UnsupportedDidMethod(String),
#[error("invalid well-known atproto-did content")]
-
#[diagnostic(code(jacquard_identity::invalid_well_known), help("expected first non-empty line to be a DID"))]
+
#[diagnostic(
+
code(jacquard_identity::invalid_well_known),
+
help("expected first non-empty line to be a DID")
+
)]
InvalidWellKnown,
#[error("missing PDS endpoint in DID document")]
#[diagnostic(code(jacquard_identity::missing_pds_endpoint))]
MissingPdsEndpoint,
#[error("HTTP error: {0}")]
-
#[diagnostic(code(jacquard_identity::http), help("check network connectivity and TLS configuration"))]
+
#[diagnostic(
+
code(jacquard_identity::http),
+
help("check network connectivity and TLS configuration")
+
)]
Http(#[from] TransportError),
#[error("HTTP status {0}")]
-
#[diagnostic(code(jacquard_identity::http_status), help("verify well-known paths or PDS XRPC endpoints"))]
+
#[diagnostic(
+
code(jacquard_identity::http_status),
+
help("verify well-known paths or PDS XRPC endpoints")
+
)]
HttpStatus(StatusCode),
#[error("XRPC error: {0}")]
-
#[diagnostic(code(jacquard_identity::xrpc), help("enable PDS fallback or public resolver if needed"))]
+
#[diagnostic(
+
code(jacquard_identity::xrpc),
+
help("enable PDS fallback or public resolver if needed")
+
)]
Xrpc(String),
#[error("URL parse error: {0}")]
#[diagnostic(code(jacquard_identity::url))]
···
#[diagnostic(code(jacquard_identity::serde))]
Serde(#[from] serde_json::Error),
#[error("invalid DID document: {0}")]
-
#[diagnostic(code(jacquard_identity::invalid_doc), help("validate keys and services; ensure AtprotoPersonalDataServer service exists"))]
+
#[diagnostic(
+
code(jacquard_identity::invalid_doc),
+
help("validate keys and services; ensure AtprotoPersonalDataServer service exists")
+
)]
InvalidDoc(String),
#[error(transparent)]
#[diagnostic(code(jacquard_identity::data))]
Data(#[from] AtDataError),
/// DID document id did not match requested DID; includes the fetched document
#[error("DID doc id mismatch")]
-
#[diagnostic(code(jacquard_identity::doc_id_mismatch), help("document id differs from requested DID; do not trust this document"))]
+
#[diagnostic(
+
code(jacquard_identity::doc_id_mismatch),
+
help("document id differs from requested DID; do not trust this document")
+
)]
DocIdMismatch {
expected: Did<'static>,
doc: DidDocument<'static>,
+6 -13
crates/jacquard/src/client/credential_session.rs
···
// Under Tokio, use `block_in_place` to make a blocking RwLock read safe.
if tokio::runtime::Handle::try_current().is_ok() {
tokio::task::block_in_place(|| {
-
self.endpoint
-
.blocking_read()
-
.clone()
-
.unwrap_or(
-
Url::parse("https://public.bsky.app")
-
.expect("public appview should be valid url"),
-
)
-
})
-
} else {
-
self.endpoint
-
.blocking_read()
-
.clone()
-
.unwrap_or(
+
self.endpoint.blocking_read().clone().unwrap_or(
Url::parse("https://public.bsky.app")
.expect("public appview should be valid url"),
)
+
})
+
} else {
+
self.endpoint.blocking_read().clone().unwrap_or(
+
Url::parse("https://public.bsky.app").expect("public appview should be valid url"),
+
)
}
}
async fn send<R: jacquard_common::types::xrpc::XrpcRequest + Send>(
+10 -5
crates/jacquard/src/main.rs
···
-
use std::sync::Arc;
use clap::Parser;
use jacquard::CowStr;
use jacquard::api::app_bsky::feed::get_timeline::GetTimeline;
use jacquard::client::credential_session::{CredentialSession, SessionKey};
use jacquard::client::{AtpSession, MemorySessionStore};
-
use jacquard::identity::PublicResolver as JacquardResolver;
use jacquard::types::xrpc::XrpcClient;
+
use jacquard_identity::slingshot_resolver_default;
use miette::IntoDiagnostic;
+
use std::sync::Arc;
#[derive(Parser, Debug)]
#[command(author, version, about = "Jacquard - AT Protocol client demo")]
···
let args = Args::parse();
// Resolver + in-memory store
-
let resolver = Arc::new(JacquardResolver::default());
+
let resolver = Arc::new(slingshot_resolver_default());
let store: Arc<MemorySessionStore<SessionKey, AtpSession>> = Arc::new(Default::default());
let client = Arc::new(resolver.clone());
let session = CredentialSession::new(store, client);
-
// Login; resolves PDS from handle/DID automatically. Persisted under (did, "session").
let _ = session
-
.login(args.username.clone(), args.password.clone(), None, None, None)
+
.login(
+
args.username.clone(),
+
args.password.clone(),
+
None,
+
None,
+
None,
+
)
.await
.into_diagnostic()?;