A better Rust ATProto crate

direct fetch for labeler defs

+58 -4
+1 -2
crates/jacquard/Cargo.toml
··· 20 20 api_bluesky = ["api", "jacquard-api/bluesky"] 21 21 # Bluesky API bindings, plus a curated selection of community lexicons 22 22 api_full = [ 23 - "api", 24 - "jacquard-api/bluesky", 23 + "api_bluesky", 25 24 "jacquard-api/other", 26 25 "jacquard-api/lexicon_community", 27 26 ]
+1 -1
crates/jacquard/src/moderation.rs
··· 38 38 39 39 pub use decision::{ModerationIterExt, moderate, moderate_all}; 40 40 #[cfg(feature = "api_bluesky")] 41 - pub use fetch::fetch_labeler_defs; 41 + pub use fetch::{fetch_labeler_defs, fetch_labeler_defs_direct}; 42 42 pub use labeled::Labeled; 43 43 pub use moderatable::Moderateable; 44 44 pub use types::{
+55
crates/jacquard/src/moderation/fetch.rs
··· 1 1 use super::LabelerDefs; 2 + use crate::client::AgentSessionExt; 2 3 use jacquard_api::app_bsky::labeler::get_services::{GetServices, GetServicesOutput}; 4 + use jacquard_api::app_bsky::labeler::service::Service; 3 5 use jacquard_common::IntoStatic; 4 6 use jacquard_common::error::ClientError; 5 7 use jacquard_common::types::string::Did; ··· 77 79 78 80 Ok(defs) 79 81 } 82 + 83 + /// Fetch labeler definitions directly from each labeler's PDS 84 + /// 85 + /// This fetches the `app.bsky.labeler.service` record directly from the PDS where 86 + /// the labeler is hosted. 87 + /// 88 + /// # Arguments 89 + /// 90 + /// * `client` - Any XRPC client with fetch_record support (Agent, etc.) 91 + /// * `dids` - List of labeler DIDs to fetch definitions for 92 + /// 93 + /// # Example 94 + /// 95 + /// ```no_run 96 + /// # use jacquard::moderation::fetch_labeler_defs_direct; 97 + /// # use jacquard::client::BasicClient; 98 + /// # use jacquard::prelude::*; 99 + /// # use jacquard_common::types::string::Did; 100 + /// # #[tokio::main] 101 + /// # async fn main() -> Result<(), Box<dyn std::error::Error>> { 102 + /// # let client = BasicClient::unauthenticated(); 103 + /// let labeler_did = Did::new_static("did:plc:ar7c4by46qjdydhdevvrndac").unwrap(); 104 + /// let defs = fetch_labeler_defs_direct(&client, vec![labeler_did]).await?; 105 + /// # Ok(()) 106 + /// # } 107 + /// ``` 108 + pub async fn fetch_labeler_defs_direct( 109 + client: &(impl AgentSessionExt + Sync), 110 + dids: Vec<Did<'_>>, 111 + ) -> Result<LabelerDefs<'static>, ClientError> { 112 + #[cfg(feature = "tracing")] 113 + let _span = tracing::debug_span!("fetch_labeler_defs_direct", count = dids.len()).entered(); 114 + 115 + let mut defs = LabelerDefs::new(); 116 + 117 + for did in dids { 118 + let uri = format!("at://{}/app.bsky.labeler.service/self", did.as_str()); 119 + let record_uri = Service::uri(uri).map_err(|e| { 120 + ClientError::Transport(jacquard_common::error::TransportError::Other( 121 + format!("Invalid URI: {}", e).into(), 122 + )) 123 + })?; 124 + 125 + let output = client.fetch_record(&record_uri).await?; 126 + let service: Service<'static> = output.value; 127 + 128 + if let Some(label_value_definitions) = service.policies.label_value_definitions { 129 + defs.insert(did.into_static(), label_value_definitions); 130 + } 131 + } 132 + 133 + Ok(defs) 134 + }
+1 -1
justfile
··· 7 7 8 8 # Check that jacquard-common compiles for wasm32 9 9 check-wasm: 10 - cargo build --target wasm32-unknown-unknown -p jacquard-common --no-default-features --features websocket 10 + cargo build --target wasm32-unknown-unknown -p jacquard-common --features websocket 11 11 12 12 # Run 'cargo run' on the project 13 13 run *ARGS: