A better Rust ATProto crate

add xrpc StreamingResponse type

Orual 57910273 0b3a649c

+92
+3
crates/jacquard-common/src/lib.rs
··· 227 227 #[cfg(feature = "streaming")] 228 228 pub use stream::{ByteStream, ByteSink, StreamError, StreamErrorKind}; 229 229 230 + #[cfg(feature = "streaming")] 231 + pub use xrpc::StreamingResponse; 232 + 230 233 pub use types::value::*; 231 234 232 235 /// Authorization token types for XRPC requests.
+7
crates/jacquard-common/src/xrpc.rs
··· 9 9 //! can inspect `error="invalid_token"` or `error="use_dpop_nonce"` and refresh/retry. 10 10 //! If the header is absent, parse the body and map auth errors to 11 11 //! `AuthError::TokenExpired`/`InvalidToken`. 12 + 13 + #[cfg(feature = "streaming")] 14 + pub mod streaming; 15 + 16 + #[cfg(feature = "streaming")] 17 + pub use streaming::StreamingResponse; 18 + 12 19 use bytes::Bytes; 13 20 use http::{ 14 21 HeaderName, HeaderValue, Request, StatusCode,
+82
crates/jacquard-common/src/xrpc/streaming.rs
··· 1 + //! Streaming support for XRPC requests and responses 2 + 3 + use crate::stream::ByteStream; 4 + use http::StatusCode; 5 + 6 + /// XRPC streaming response 7 + /// 8 + /// Similar to `Response<R>` but holds a streaming body instead of a buffer. 9 + pub struct StreamingResponse { 10 + parts: http::response::Parts, 11 + body: ByteStream, 12 + } 13 + 14 + impl StreamingResponse { 15 + /// Create a new streaming response 16 + pub fn new(parts: http::response::Parts, body: ByteStream) -> Self { 17 + Self { parts, body } 18 + } 19 + 20 + /// Get the HTTP status code 21 + pub fn status(&self) -> StatusCode { 22 + self.parts.status 23 + } 24 + 25 + /// Get the response headers 26 + pub fn headers(&self) -> &http::HeaderMap { 27 + &self.parts.headers 28 + } 29 + 30 + /// Get the response version 31 + pub fn version(&self) -> http::Version { 32 + self.parts.version 33 + } 34 + 35 + /// Consume the response and return parts and body separately 36 + pub fn into_parts(self) -> (http::response::Parts, ByteStream) { 37 + (self.parts, self.body) 38 + } 39 + 40 + /// Get mutable access to the body stream 41 + pub fn body_mut(&mut self) -> &mut ByteStream { 42 + &mut self.body 43 + } 44 + 45 + /// Get a reference to the body stream 46 + pub fn body(&self) -> &ByteStream { 47 + &self.body 48 + } 49 + } 50 + 51 + impl std::fmt::Debug for StreamingResponse { 52 + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 53 + f.debug_struct("StreamingResponse") 54 + .field("status", &self.parts.status) 55 + .field("version", &self.parts.version) 56 + .field("headers", &self.parts.headers) 57 + .finish_non_exhaustive() 58 + } 59 + } 60 + 61 + #[cfg(test)] 62 + mod tests { 63 + use super::*; 64 + use bytes::Bytes; 65 + use futures::stream; 66 + 67 + #[test] 68 + fn streaming_response_holds_parts_and_body() { 69 + // Build parts from a Response and extract them 70 + let response = http::Response::builder() 71 + .status(StatusCode::OK) 72 + .body(()) 73 + .unwrap(); 74 + let (parts, _) = response.into_parts(); 75 + 76 + let stream = stream::iter(vec![Ok(Bytes::from("test"))]); 77 + let body = ByteStream::new(stream); 78 + 79 + let response = StreamingResponse::new(parts, body); 80 + assert_eq!(response.status(), StatusCode::OK); 81 + } 82 + }