A better Rust ATProto crate

add streaming examples and module documentation

Orual fa087a22 92910c6f

Changed files
+105 -1
crates
+1
Cargo.lock
··· 1982 "cid", 1983 "ed25519-dalek", 1984 "futures", 1985 "getrandom 0.3.4", 1986 "http", 1987 "ipld-core",
··· 1982 "cid", 1983 "ed25519-dalek", 1984 "futures", 1985 + "futures-lite", 1986 "getrandom 0.3.4", 1987 "http", 1988 "ipld-core",
+2 -1
crates/jacquard-common/Cargo.toml
··· 81 features = ["arithmetic"] 82 83 [dev-dependencies] 84 - tokio = { version = "1", features = ["macros", "rt"] } 85 86 [package.metadata.docs.rs] 87 features = [ "crypto-k256", "crypto-k256", "crypto-p256"]
··· 81 features = ["arithmetic"] 82 83 [dev-dependencies] 84 + tokio = { version = "1", features = ["macros", "rt", "rt-multi-thread"] } 85 + futures-lite = "2.6" 86 87 [package.metadata.docs.rs] 88 features = [ "crypto-k256", "crypto-k256", "crypto-p256"]
+27
crates/jacquard-common/examples/streaming_download.rs
···
··· 1 + //! Example: Download large file using streaming 2 + #![cfg(all(feature = "streaming", feature = "reqwest-client"))] 3 + 4 + use jacquard_common::http_client::HttpClientExt; 5 + 6 + #[tokio::main] 7 + async fn main() -> Result<(), Box<dyn std::error::Error>> { 8 + let client = reqwest::Client::new(); 9 + 10 + let request = http::Request::builder() 11 + .uri("https://httpbin.org/bytes/1024") 12 + .body(vec![]) 13 + .unwrap(); 14 + 15 + let response = client.send_http_streaming(request).await?; 16 + println!("Status: {}", response.status()); 17 + println!("Headers: {:?}", response.headers()); 18 + 19 + let (_parts, _body) = response.into_parts(); 20 + println!("Received streaming response body (ByteStream)"); 21 + 22 + // Note: To iterate over chunks, use futures_lite::StreamExt on the pinned inner stream: 23 + // let mut stream = Box::pin(body.into_inner()); 24 + // while let Some(chunk) = stream.as_mut().try_next().await? { ... } 25 + 26 + Ok(()) 27 + }
+33
crates/jacquard-common/examples/streaming_upload.rs
···
··· 1 + //! Example: Upload data using streaming request body 2 + #![cfg(all(feature = "streaming", feature = "reqwest-client"))] 3 + 4 + use jacquard_common::http_client::HttpClientExt; 5 + use futures::stream; 6 + use bytes::Bytes; 7 + 8 + #[tokio::main] 9 + async fn main() -> Result<(), Box<dyn std::error::Error>> { 10 + let client = reqwest::Client::new(); 11 + 12 + // Create a stream of data chunks 13 + let chunks = vec![ 14 + Bytes::from("Hello, "), 15 + Bytes::from("streaming "), 16 + Bytes::from("world!"), 17 + ]; 18 + let body_stream = stream::iter(chunks); 19 + 20 + // Build request and split into parts 21 + let request = http::Request::builder() 22 + .method(http::Method::POST) 23 + .uri("https://httpbin.org/post") 24 + .body(()) 25 + .unwrap(); 26 + 27 + let (parts, _) = request.into_parts(); 28 + 29 + let response = client.send_http_bidirectional(parts, body_stream).await?; 30 + println!("Status: {}", response.status()); 31 + 32 + Ok(()) 33 + }
+42
crates/jacquard-common/src/stream.rs
··· 1 //! Stream abstractions for HTTP request/response bodies 2 3 use std::error::Error; 4 use std::fmt;
··· 1 //! Stream abstractions for HTTP request/response bodies 2 + //! 3 + //! This module provides platform-agnostic streaming types for handling large 4 + //! payloads efficiently without loading everything into memory. 5 + //! 6 + //! # Features 7 + //! 8 + //! - [`ByteStream`]: Streaming response bodies 9 + //! - [`ByteSink`]: Streaming request bodies 10 + //! - [`StreamError`]: Concrete error type for streaming operations 11 + //! 12 + //! # Platform Support 13 + //! 14 + //! Uses `n0-future` for platform-agnostic async streams that work on both 15 + //! native and WASM targets without requiring `Send` bounds on WASM. 16 + //! 17 + //! # Examples 18 + //! 19 + //! ## Streaming Download 20 + //! 21 + //! ```no_run 22 + //! # #[cfg(all(feature = "streaming", feature = "reqwest-client"))] 23 + //! # async fn example() -> Result<(), Box<dyn std::error::Error>> { 24 + //! use jacquard_common::http_client::{HttpClient, HttpClientExt}; 25 + //! use futures_lite::StreamExt; 26 + //! 27 + //! let client = reqwest::Client::new(); 28 + //! let request = http::Request::builder() 29 + //! .uri("https://example.com/large-file") 30 + //! .body(vec![]) 31 + //! .unwrap(); 32 + //! 33 + //! let response = client.send_http_streaming(request).await?; 34 + //! let (_parts, body) = response.into_parts(); 35 + //! let mut stream = Box::pin(body.into_inner()); 36 + //! 37 + //! // Use futures_lite::StreamExt for iteration 38 + //! while let Some(chunk) = stream.as_mut().try_next().await? { 39 + //! // Process chunk without loading entire file into memory 40 + //! } 41 + //! # Ok(()) 42 + //! # } 43 + //! ``` 44 45 use std::error::Error; 46 use std::fmt;