diff --git a/Cargo.lock b/Cargo.lock index 4b1efc30..6f1a283c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2545,7 +2545,6 @@ dependencies = [ "bytes", "hickory-resolver", "http", - "jacquard-api", "jacquard-common", "jacquard-lexicon", "miette", @@ -2570,7 +2569,6 @@ dependencies = [ "clap_mangen", "glob", "inventory", - "jacquard-api", "jacquard-common", "jacquard-derive", "jacquard-identity", diff --git a/crates/jacquard-common/src/xrpc/atproto.rs b/crates/jacquard-common/src/xrpc/atproto.rs index 8671dd7e..f53119e2 100644 --- a/crates/jacquard-common/src/xrpc/atproto.rs +++ b/crates/jacquard-common/src/xrpc/atproto.rs @@ -4,14 +4,15 @@ //! between jacquard-lexgen/jacquard-identity and jacquard-api. They provide minimal //! implementations sufficient for bootstrap code generation without builders or //! validation helpers. - -use alloc::vec::Vec; +//! use crate::CowStr; use crate::IntoStatic; -use crate::types::string::{AtUri, Cid, Did, Handle, Nsid}; use crate::types::ident::AtIdentifier; +use crate::types::string::{AtUri, Cid, Did, Handle, Nsid}; use crate::types::value::Data; use crate::xrpc::{GenericError, XrpcMethod, XrpcRequest, XrpcResp}; + +use alloc::vec::Vec; use core::error::Error; use core::fmt::{self, Display}; use serde::{Deserialize, Serialize}; @@ -21,15 +22,9 @@ use serde::{Deserialize, Serialize}; // ============================================================================ /// Request for com.atproto.repo.listRecords. -#[derive( - Serialize, - Deserialize, - Debug, - Clone, - PartialEq, - Eq, -)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] +#[allow(missing_docs)] pub struct ListRecords<'a> { #[serde(borrow)] pub collection: Nsid<'a>, @@ -59,15 +54,9 @@ impl IntoStatic for ListRecords<'_> { } /// Output for com.atproto.repo.listRecords. -#[derive( - Serialize, - Deserialize, - Debug, - Clone, - PartialEq, - Eq, -)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] +#[allow(missing_docs)] pub struct ListRecordsOutput<'a> { #[serde(skip_serializing_if = "Option::is_none")] #[serde(borrow)] @@ -88,15 +77,9 @@ impl IntoStatic for ListRecordsOutput<'_> { } /// A single record in a list response. -#[derive( - Serialize, - Deserialize, - Debug, - Clone, - PartialEq, - Eq, -)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] +#[allow(missing_docs)] pub struct ListRecordsRecord<'a> { #[serde(skip_serializing_if = "Option::is_none")] #[serde(borrow)] @@ -140,14 +123,8 @@ impl<'a> XrpcRequest for ListRecords<'a> { // ============================================================================ /// Request for com.atproto.repo.getRecord. -#[derive( - Serialize, - Deserialize, - Debug, - Clone, - PartialEq, - Eq, -)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +#[allow(missing_docs)] #[serde(rename_all = "camelCase")] pub struct GetRecord<'a> { #[serde(skip_serializing_if = "Option::is_none")] @@ -175,14 +152,8 @@ impl IntoStatic for GetRecord<'_> { } /// Output for com.atproto.repo.getRecord. -#[derive( - Serialize, - Deserialize, - Debug, - Clone, - PartialEq, - Eq, -)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +#[allow(missing_docs)] #[serde(rename_all = "camelCase")] pub struct GetRecordOutput<'a> { #[serde(skip_serializing_if = "Option::is_none")] @@ -207,14 +178,8 @@ impl IntoStatic for GetRecordOutput<'_> { } /// Error type for com.atproto.repo.getRecord. -#[derive( - Serialize, - Deserialize, - Debug, - Clone, - PartialEq, - Eq, -)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +#[allow(missing_docs)] #[serde(tag = "error", content = "message")] #[serde(bound(deserialize = "'de: 'a"))] pub enum GetRecordError<'a> { @@ -269,15 +234,9 @@ impl<'a> XrpcRequest for GetRecord<'a> { // ============================================================================ /// Request for com.atproto.identity.resolveHandle. -#[derive( - Serialize, - Deserialize, - Debug, - Clone, - PartialEq, - Eq, -)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] +#[allow(missing_docs)] pub struct ResolveHandle<'a> { #[serde(borrow)] pub handle: Handle<'a>, @@ -294,15 +253,9 @@ impl IntoStatic for ResolveHandle<'_> { } /// Output for com.atproto.identity.resolveHandle. -#[derive( - Serialize, - Deserialize, - Debug, - Clone, - PartialEq, - Eq, -)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] +#[allow(missing_docs)] pub struct ResolveHandleOutput<'a> { #[serde(borrow)] pub did: Did<'a>, @@ -319,15 +272,9 @@ impl IntoStatic for ResolveHandleOutput<'_> { } /// Error type for com.atproto.identity.resolveHandle. -#[derive( - Serialize, - Deserialize, - Debug, - Clone, - PartialEq, - Eq, -)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(tag = "error", content = "message")] +#[allow(missing_docs)] #[serde(bound(deserialize = "'de: 'a"))] pub enum ResolveHandleError<'a> { #[serde(rename = "HandleNotFound")] @@ -381,15 +328,9 @@ impl<'a> XrpcRequest for ResolveHandle<'a> { // ============================================================================ /// Request for com.atproto.identity.resolveDid. -#[derive( - Serialize, - Deserialize, - Debug, - Clone, - PartialEq, - Eq, -)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] +#[allow(missing_docs)] pub struct ResolveDid<'a> { #[serde(borrow)] pub did: Did<'a>, @@ -406,15 +347,9 @@ impl IntoStatic for ResolveDid<'_> { } /// Output for com.atproto.identity.resolveDid. -#[derive( - Serialize, - Deserialize, - Debug, - Clone, - PartialEq, - Eq, -)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] +#[allow(missing_docs)] pub struct ResolveDidOutput<'a> { #[serde(borrow)] pub did_doc: Data<'a>, @@ -431,16 +366,10 @@ impl IntoStatic for ResolveDidOutput<'_> { } /// Error type for com.atproto.identity.resolveDid. -#[derive( - Serialize, - Deserialize, - Debug, - Clone, - PartialEq, - Eq, -)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(tag = "error", content = "message")] #[serde(bound(deserialize = "'de: 'a"))] +#[allow(missing_docs)] pub enum ResolveDidError<'a> { #[serde(rename = "DidNotFound")] DidNotFound(Option>), @@ -510,7 +439,10 @@ mod tests { #[test] fn test_list_records_serializes() { let req = ListRecords { - repo: AtIdentifier::new("test.bsky.social").unwrap().into_static().into(), + repo: AtIdentifier::new("test.bsky.social") + .unwrap() + .into_static() + .into(), collection: Nsid::new("app.bsky.feed.post").unwrap().into_static(), cursor: None, limit: Some(50), @@ -602,7 +534,10 @@ mod tests { #[test] fn test_types_implement_into_static() { let list_records = ListRecords { - repo: AtIdentifier::new("test.bsky.social").unwrap().into_static().into(), + repo: AtIdentifier::new("test.bsky.social") + .unwrap() + .into_static() + .into(), collection: Nsid::new("app.bsky.feed.post").unwrap().into_static(), cursor: None, limit: Some(50), @@ -611,7 +546,10 @@ mod tests { let _static = list_records.into_static(); let get_record = GetRecord { - repo: AtIdentifier::new("test.bsky.social").unwrap().into_static().into(), + repo: AtIdentifier::new("test.bsky.social") + .unwrap() + .into_static() + .into(), collection: Nsid::new("app.bsky.feed.post").unwrap().into_static(), rkey: CowStr::from("abc123").into_static(), cid: None, diff --git a/crates/jacquard-identity/Cargo.toml b/crates/jacquard-identity/Cargo.toml index 4d9bb54f..4a04645d 100644 --- a/crates/jacquard-identity/Cargo.toml +++ b/crates/jacquard-identity/Cargo.toml @@ -23,7 +23,6 @@ trait-variant.workspace = true bon.workspace = true bytes.workspace = true jacquard-common = { version = "0.10", path = "../jacquard-common", features = ["reqwest-client"] } -jacquard-api = { version = "0.10", path = "../jacquard-api", default-features = false, features = ["minimal"] } jacquard-lexicon = { version = "0.10", path = "../jacquard-lexicon", default-features = false } reqwest.workspace = true serde.workspace = true diff --git a/crates/jacquard-identity/src/lexicon_resolver.rs b/crates/jacquard-identity/src/lexicon_resolver.rs index ffc75d73..b44277dc 100644 --- a/crates/jacquard-identity/src/lexicon_resolver.rs +++ b/crates/jacquard-identity/src/lexicon_resolver.rs @@ -8,12 +8,9 @@ use crate::resolver::{IdentityError, IdentityResolver}; use jacquard_common::{ IntoStatic, - deps::fluent_uri::Uri, deps::smol_str, - from_json_value, - types::{aturi::AtUri, cid::Cid, did::Did, string::Nsid}, + types::{cid::Cid, did::Did, string::Nsid}, }; -use jacquard_lexicon::lexicon::LexiconDoc; use smol_str::SmolStr; /// Resolve lexicon authority (NSID → authoritative DID) @@ -366,100 +363,6 @@ impl crate::JacquardResolver { } } -#[allow(unused)] -use jacquard_api::com_atproto::lexicon::resolve_lexicon::{ResolveLexicon, ResolveLexiconOutput}; - -impl crate::JacquardResolver { - #[allow(dead_code)] - async fn resolve_lexicon_xrpc( - &self, - nsid: &Nsid<'_>, - ) -> std::result::Result, LexiconResolutionError> { - #[cfg(feature = "tracing")] - tracing::debug!("resolving lexicon via XRPC: {}", nsid); - - let qs = serde_html_form::to_string(&ResolveLexicon::new().nsid(nsid.clone()).build()) - .map_err(|e| LexiconResolutionError::fetch_failed(nsid.as_str(), e))?; - - let url_str = format!( - "https://public.api.bsky.app/xrpc/com.atproto.lexicon.resolveLexicon?{}", - qs - ); - let url = Uri::parse(url_str) - .map(|u| u.to_owned()) - .map_err(|(e, _)| LexiconResolutionError::fetch_failed(nsid.as_str(), e))?; - - #[cfg(feature = "tracing")] - tracing::debug!("fetching from URL: {}", url); - - let (buf, status) = self - .get_json_bytes(url.borrow()) - .await - .map_err(|e| LexiconResolutionError::fetch_failed(nsid.as_str(), e))?; - - #[cfg(feature = "tracing")] - tracing::debug!("got response with status: {}", status); - - if !status.is_success() { - return Err(LexiconResolutionError::http_error( - nsid.as_str(), - status.as_u16(), - )); - } - - let val = serde_json::from_slice::(&buf) - .map_err(|e| LexiconResolutionError::parse_failed(nsid.as_str(), e))?; - - #[cfg(feature = "tracing")] - tracing::debug!("parsed JSON response"); - - let obj = val.as_object().ok_or_else(|| { - LexiconResolutionError::resolution_failed(nsid.as_str(), "response not an object") - })?; - - let schema_val = obj.get("schema").ok_or_else(|| { - #[cfg(feature = "tracing")] - tracing::error!( - "response missing 'schema' field, got keys: {:?}", - obj.keys().collect::>() - ); - - LexiconResolutionError::missing_response_field(nsid.as_str(), "schema") - })?; - - #[cfg(feature = "tracing")] - tracing::debug!("found schema field in response"); - - let schema = from_json_value::(schema_val.clone()) - .map_err(|e| LexiconResolutionError::parse_failed(nsid.as_str(), e))?; - - let uri_str = obj - .get("uri") - .and_then(|v| v.as_str()) - .ok_or_else(|| LexiconResolutionError::missing_response_field(nsid.as_str(), "uri"))?; - - let cid_str = obj - .get("cid") - .and_then(|v| v.as_str()) - .ok_or_else(|| LexiconResolutionError::missing_response_field(nsid.as_str(), "cid"))?; - - let uri = AtUri::new_owned(uri_str) - .map_err(|e| LexiconResolutionError::parse_failed(nsid.as_str(), e))?; - - let cid = Cid::str(cid_str).into_static(); - let repo = Did::raw(uri.authority().as_str()).into_static(); - - #[cfg(feature = "tracing")] - tracing::debug!("successfully resolved lexicon schema for {}", nsid); - - Ok(ResolvedLexiconSchema { - repo, - cid, - nsid: nsid.clone().into_static(), - doc: schema.into_static(), - }) - } -} #[cfg(all(feature = "dns", not(target_family = "wasm")))] impl LexiconAuthorityResolver for crate::JacquardResolver { @@ -585,7 +488,7 @@ impl LexiconSchemaResolver for crate::JacquardResolver { &self, nsid: &Nsid<'_>, ) -> std::result::Result, LexiconResolutionError> { - use jacquard_api::com_atproto::repo::get_record::GetRecord; + use jacquard_common::xrpc::atproto::GetRecord; use jacquard_common::{IntoStatic, xrpc::XrpcExt}; // Try cache first @@ -624,11 +527,12 @@ impl LexiconSchemaResolver for crate::JacquardResolver { let collection = Nsid::new("com.atproto.lexicon.schema") .map_err(|_| LexiconResolutionError::invalid_collection())?; - let request = GetRecord::new() - .repo(authority_did.clone()) - .collection(collection.into_static()) - .rkey(nsid.clone()) - .build(); + let request = GetRecord { + repo: authority_did.clone().into(), + collection: collection.into_static(), + rkey: nsid.clone().into(), + cid: None, + }; let response = self .xrpc(pds) diff --git a/crates/jacquard-identity/src/lib.rs b/crates/jacquard-identity/src/lib.rs index 06782328..1757d2af 100644 --- a/crates/jacquard-identity/src/lib.rs +++ b/crates/jacquard-identity/src/lib.rs @@ -75,8 +75,7 @@ use crate::resolver::{ ResolverOptions, }; use bytes::Bytes; -use jacquard_api::com_atproto::identity::resolve_did; -use jacquard_api::com_atproto::identity::resolve_handle::ResolveHandle; +use jacquard_common::xrpc::atproto::{ResolveDid, ResolveHandle}; #[cfg(feature = "streaming")] use jacquard_common::ByteStream; use jacquard_common::deps::fluent_uri::Uri; @@ -577,9 +576,9 @@ impl JacquardResolver { Some(u) => u.clone(), None => return Err(IdentityError::no_pds_fallback()), }; - let req = ResolveHandle::new() - .handle(handle.clone().into_static()) - .build(); + let req = ResolveHandle { + handle: handle.clone().into_static(), + }; let resp = self.http.xrpc(pds).send(&req).await.map_err(|e| { IdentityError::from(e).with_context(format!("resolving handle {}", handle)) })?; @@ -608,7 +607,9 @@ impl JacquardResolver { Some(u) => u.clone(), None => return Err(IdentityError::no_pds_fallback()), }; - let req = resolve_did::ResolveDid::new().did(did.clone()).build(); + let req = ResolveDid { + did: did.clone(), + }; let resp = self.http.xrpc(pds).send(&req).await.map_err(|e| { IdentityError::from(e).with_context(format!("fetching DID doc for {}", did)) })?; @@ -640,9 +641,9 @@ impl JacquardResolver { }; // Build URL using string manipulation, then parse let qs = serde_html_form::to_string( - &resolve_did::ResolveDid::new() - .did(did.clone().into_static()) - .build(), + &ResolveDid { + did: did.clone().into_static(), + }, ) .unwrap_or_default(); let url_str = if qs.is_empty() { @@ -722,7 +723,9 @@ impl IdentityResolver for JacquardResolver { // Public unauth fallback if self.opts.public_fallback_for_handle { if let Ok(qs) = serde_html_form::to_string( - &ResolveHandle::new().handle((*handle).clone()).build(), + &ResolveHandle { + handle: (*handle).clone(), + }, ) { let url_str = format!( "https://public.api.bsky.app/xrpc/com.atproto.identity.resolveHandle?{}", @@ -756,7 +759,9 @@ impl IdentityResolver for JacquardResolver { // Non-auth path: if PlcSource is Slingshot, use its resolveHandle endpoint. if let PlcSource::Slingshot { base } = &self.opts.plc_source { let qs = serde_html_form::to_string( - &ResolveHandle::new().handle((*handle).clone()).build(), + &ResolveHandle { + handle: (*handle).clone(), + }, ) .unwrap_or_default(); let url_str = if qs.is_empty() { diff --git a/crates/jacquard-lexgen/Cargo.toml b/crates/jacquard-lexgen/Cargo.toml index 8066f8cb..00650a71 100644 --- a/crates/jacquard-lexgen/Cargo.toml +++ b/crates/jacquard-lexgen/Cargo.toml @@ -32,7 +32,6 @@ path = "../../examples/extract_inventory.rs" clap.workspace = true glob = "0.3" inventory = "0.3" -jacquard-api = { version = "0.10", path = "../jacquard-api", default-features = false, features = [ "minimal" ] } jacquard-common = { version = "0.10", features = [ "reqwest-client" ], path = "../jacquard-common" } jacquard-derive = { version = "0.10", path = "../jacquard-derive" } jacquard-identity = { version = "0.10", path = "../jacquard-identity", features = ["dns"] } diff --git a/crates/jacquard-lexgen/src/fetch/sources/atproto.rs b/crates/jacquard-lexgen/src/fetch/sources/atproto.rs index 55df87a9..a207fb1c 100644 --- a/crates/jacquard-lexgen/src/fetch/sources/atproto.rs +++ b/crates/jacquard-lexgen/src/fetch/sources/atproto.rs @@ -1,5 +1,5 @@ use super::LexiconSource; -use jacquard_api::com_atproto::repo::list_records::{ListRecords, Record}; +use jacquard_common::xrpc::atproto::{ListRecords, ListRecordsRecord}; use jacquard_common::types::ident::AtIdentifier; use jacquard_common::types::string::Nsid; use jacquard_common::xrpc::XrpcExt; @@ -35,7 +35,7 @@ impl AtProtoSource { Ok(lexicons) } - fn parse_lexicon_record(record_data: &Record<'_>) -> Option> { + fn parse_lexicon_record(record_data: &ListRecordsRecord<'_>) -> Option> { // // Extract the 'value' field from the record // let value = match record_data { // jacquard_common::types::value::Data::Object(map) => map.0.get("value")?, @@ -104,10 +104,13 @@ impl LexiconSource for AtProtoSource { let mut lexicons = HashMap::new(); // Try to fetch all records at once first - let req = ListRecords::new() - .repo(repo.clone().into_static()) - .collection(collection.clone().into_static()) - .build(); + let req = ListRecords { + repo: repo.clone().into_static().into(), + collection: collection.clone().into_static(), + cursor: None, + limit: None, + reverse: None, + }; let resp = resolver.xrpc(pds.clone()).send(&req).await?; @@ -128,19 +131,12 @@ impl LexiconSource for AtProtoSource { let mut cursor: Option = None; loop { - let req = if let Some(ref c) = cursor { - ListRecords::new() - .repo(repo.clone().into_static()) - .collection(collection.clone().into_static()) - .limit(1) - .cursor(c.clone()) - .build() - } else { - ListRecords::new() - .repo(repo.clone().into_static()) - .collection(collection.clone().into_static()) - .limit(1) - .build() + let req = ListRecords { + repo: repo.clone().into_static().into(), + collection: collection.clone().into_static(), + cursor: cursor.clone(), + limit: Some(1), + reverse: None, }; let resp = resolver.xrpc(pds.clone()).send(&req).await?;