A better Rust ATProto crate

fix for SessionKey lifetime nonense?

Changed files
+16 -17
crates
+4 -1
Cargo.toml
··· 33 serde_html_form = "0.2" 34 serde_ipld_dagcbor = "0.6" 35 serde_repr = "0.1" 36 37 # Error handling 38 miette = "7.6" ··· 88 jose-jwk = "0.1" 89 90 # Text processing 91 - regex = { version = "1.11", default-features = false } 92 webpage = { version = "2.0", default-features = false }
··· 33 serde_html_form = "0.2" 34 serde_ipld_dagcbor = "0.6" 35 serde_repr = "0.1" 36 + facet = "0.6" 37 + facet-json = "0.6" 38 + cfg-if = "1.0" 39 40 # Error handling 41 miette = "7.6" ··· 91 jose-jwk = "0.1" 92 93 # Text processing 94 + regex = { version = "1.12", default-features = false } 95 webpage = { version = "2.0", default-features = false }
+1 -6
crates/jacquard-lexicon/src/codegen/structs.rs
··· 340 let rust_type = if is_required { 341 rust_type 342 } else { 343 - // Use std::option::Option for non-builder structs to avoid name collision 344 - if is_builder { 345 - quote! { Option<#rust_type> } 346 - } else { 347 - quote! { std::option::Option<#rust_type> } 348 - } 349 }; 350 351 // Extract description from field type
··· 340 let rust_type = if is_required { 341 rust_type 342 } else { 343 + quote! { std::option::Option<#rust_type> } 344 }; 345 346 // Extract description from field type
+1 -1
crates/jacquard/src/client.rs
··· 1206 async move { 1207 CredentialSession::<S, T, W>::session_info(self) 1208 .await 1209 - .map(|(did, sid)| (did, Some(sid))) 1210 } 1211 } 1212 fn endpoint(&self) -> impl Future<Output = CowStr<'static>> {
··· 1206 async move { 1207 CredentialSession::<S, T, W>::session_info(self) 1208 .await 1209 + .map(|key| (key.0, Some(key.1))) 1210 } 1211 } 1212 fn endpoint(&self) -> impl Future<Output = CowStr<'static>> {
+6 -5
crates/jacquard/src/client/credential_session.rs
··· 30 use jacquard_common::xrpc::XrpcSubscription; 31 32 /// Storage key for app‑password sessions: `(account DID, session id)`. 33 - pub type SessionKey = (Did<'static>, CowStr<'static>); 34 35 /// Stateful client for app‑password based sessions. 36 /// ··· 273 let session = AtpSession::from(out); 274 275 let sid = session_id.unwrap_or_else(|| CowStr::new_static("session")); 276 - let key = (session.did.clone(), sid.into_static()); 277 self.store 278 .set(key.clone(), session.clone()) 279 .await ··· 306 tracing::info_span!("credential_session_restore", did = %did, session_id = %session_id) 307 .entered(); 308 309 - let key = (did.clone().into_static(), session_id.clone().into_static()); 310 let Some(sess) = self.store.get(&key).await else { 311 return Err(ClientError::auth(AuthError::NotAuthenticated)); 312 }; ··· 331 *self.endpoint.write().await = Some(pds.to_cowstr().into_static()); 332 // ensure store has the session (no-op if it existed) 333 self.store 334 - .set((sess.did.clone(), session_id.into_static()), sess) 335 .await?; 336 if let Some(file_store) = 337 (&*self.store as &dyn Any).downcast_ref::<crate::client::token::FileAuthStore>() ··· 356 where 357 S: Any + 'static, 358 { 359 - let key = (did.clone().into_static(), session_id.into_static()); 360 if self.store.get(&key).await.is_none() { 361 return Err(ClientError::auth(AuthError::NotAuthenticated)); 362 }
··· 30 use jacquard_common::xrpc::XrpcSubscription; 31 32 /// Storage key for app‑password sessions: `(account DID, session id)`. 33 + #[derive(Debug, Clone, PartialEq, Eq, Hash)] 34 + pub struct SessionKey(pub Did<'static>, pub CowStr<'static>); 35 36 /// Stateful client for app‑password based sessions. 37 /// ··· 274 let session = AtpSession::from(out); 275 276 let sid = session_id.unwrap_or_else(|| CowStr::new_static("session")); 277 + let key = SessionKey(session.did.clone(), sid.into_static()); 278 self.store 279 .set(key.clone(), session.clone()) 280 .await ··· 307 tracing::info_span!("credential_session_restore", did = %did, session_id = %session_id) 308 .entered(); 309 310 + let key = SessionKey(did.clone().into_static(), session_id.clone().into_static()); 311 let Some(sess) = self.store.get(&key).await else { 312 return Err(ClientError::auth(AuthError::NotAuthenticated)); 313 }; ··· 332 *self.endpoint.write().await = Some(pds.to_cowstr().into_static()); 333 // ensure store has the session (no-op if it existed) 334 self.store 335 + .set(SessionKey(sess.did.clone(), session_id.into_static()), sess) 336 .await?; 337 if let Some(file_store) = 338 (&*self.store as &dyn Any).downcast_ref::<crate::client::token::FileAuthStore>() ··· 357 where 358 S: Any + 'static, 359 { 360 + let key = SessionKey(did.clone().into_static(), session_id.into_static()); 361 if self.store.get(&key).await.is_none() { 362 return Err(ClientError::auth(AuthError::NotAuthenticated)); 363 }
+1 -1
crates/jacquard/src/client/token.rs
··· 475 did: Did::new_static("did:plc:alice").unwrap(), 476 handle: Handle::new_static("alice.bsky.social").unwrap(), 477 }; 478 - let key: SessionKey = (session.did.clone(), "session".into()); 479 jacquard_common::session::SessionStore::set(&store, key.clone(), session.clone()) 480 .await 481 .unwrap();
··· 475 did: Did::new_static("did:plc:alice").unwrap(), 476 handle: Handle::new_static("alice.bsky.social").unwrap(), 477 }; 478 + let key = SessionKey(session.did.clone(), "session".into()); 479 jacquard_common::session::SessionStore::set(&store, key.clone(), session.clone()) 480 .await 481 .unwrap();
+1 -1
crates/jacquard/tests/agent.rs
··· 97 did: Did::new_static("did:plc:alice").unwrap(), 98 handle: Handle::new_static("alice.bsky.social").unwrap(), 99 }; 100 - let key: SessionKey = (atp.did.clone(), "session".into()); 101 jacquard_common::session::SessionStore::set(store.as_ref(), key.clone(), atp) 102 .await 103 .unwrap();
··· 97 did: Did::new_static("did:plc:alice").unwrap(), 98 handle: Handle::new_static("alice.bsky.social").unwrap(), 99 }; 100 + let key = SessionKey(atp.did.clone(), "session".into()); 101 jacquard_common::session::SessionStore::set(store.as_ref(), key.clone(), atp) 102 .await 103 .unwrap();
+1 -1
crates/jacquard/tests/credential_session.rs
··· 247 ); 248 249 // Verify store updated with refreshed tokens 250 - let key: SessionKey = ( 251 Did::new_static("did:plc:alice").unwrap(), 252 jacquard::CowStr::from("session"), 253 );
··· 247 ); 248 249 // Verify store updated with refreshed tokens 250 + let key = SessionKey( 251 Did::new_static("did:plc:alice").unwrap(), 252 jacquard::CowStr::from("session"), 253 );
+1 -1
crates/jacquard/tests/restore_pds_cache.rs
··· 91 did: Did::new_static("did:plc:alice").unwrap(), 92 handle: Handle::new_static("alice.bsky.social").unwrap(), 93 }; 94 - let key: SessionKey = (session.did.clone(), "session".into()); 95 jacquard_common::session::SessionStore::set(store.as_ref(), key.clone(), session) 96 .await 97 .unwrap();
··· 91 did: Did::new_static("did:plc:alice").unwrap(), 92 handle: Handle::new_static("alice.bsky.social").unwrap(), 93 }; 94 + let key = SessionKey(session.did.clone(), "session".into()); 95 jacquard_common::session::SessionStore::set(store.as_ref(), key.clone(), session) 96 .await 97 .unwrap();