A better Rust ATProto crate

add StreamError type

Orual 5bf84829 63216929

+361 -2
+243 -2
Cargo.lock
··· 653 653 ] 654 654 655 655 [[package]] 656 + name = "cordyceps" 657 + version = "0.3.4" 658 + source = "registry+https://github.com/rust-lang/crates.io-index" 659 + checksum = "688d7fbb8092b8de775ef2536f36c8c31f2bc4006ece2e8d8ad2d17d00ce0a2a" 660 + dependencies = [ 661 + "loom", 662 + "tracing", 663 + ] 664 + 665 + [[package]] 656 666 name = "core-foundation" 657 667 version = "0.9.4" 658 668 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 862 872 ] 863 873 864 874 [[package]] 875 + name = "derive_more" 876 + version = "1.0.0" 877 + source = "registry+https://github.com/rust-lang/crates.io-index" 878 + checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" 879 + dependencies = [ 880 + "derive_more-impl", 881 + ] 882 + 883 + [[package]] 884 + name = "derive_more-impl" 885 + version = "1.0.0" 886 + source = "registry+https://github.com/rust-lang/crates.io-index" 887 + checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" 888 + dependencies = [ 889 + "proc-macro2", 890 + "quote", 891 + "syn 2.0.106", 892 + "unicode-xid", 893 + ] 894 + 895 + [[package]] 896 + name = "diatomic-waker" 897 + version = "0.2.3" 898 + source = "registry+https://github.com/rust-lang/crates.io-index" 899 + checksum = "ab03c107fafeb3ee9f5925686dbb7a73bc76e3932abb0d2b365cb64b169cf04c" 900 + 901 + [[package]] 865 902 name = "diff" 866 903 version = "0.1.13" 867 904 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1109 1146 ] 1110 1147 1111 1148 [[package]] 1149 + name = "futures-buffered" 1150 + version = "0.2.12" 1151 + source = "registry+https://github.com/rust-lang/crates.io-index" 1152 + checksum = "a8e0e1f38ec07ba4abbde21eed377082f17ccb988be9d988a5adbf4bafc118fd" 1153 + dependencies = [ 1154 + "cordyceps", 1155 + "diatomic-waker", 1156 + "futures-core", 1157 + "pin-project-lite", 1158 + "spin 0.10.0", 1159 + ] 1160 + 1161 + [[package]] 1112 1162 name = "futures-channel" 1113 1163 version = "0.3.31" 1114 1164 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1130 1180 checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 1131 1181 1132 1182 [[package]] 1183 + name = "futures-lite" 1184 + version = "2.6.1" 1185 + source = "registry+https://github.com/rust-lang/crates.io-index" 1186 + checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" 1187 + dependencies = [ 1188 + "fastrand", 1189 + "futures-core", 1190 + "futures-io", 1191 + "parking", 1192 + "pin-project-lite", 1193 + ] 1194 + 1195 + [[package]] 1196 + name = "futures-macro" 1197 + version = "0.3.31" 1198 + source = "registry+https://github.com/rust-lang/crates.io-index" 1199 + checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 1200 + dependencies = [ 1201 + "proc-macro2", 1202 + "quote", 1203 + "syn 2.0.106", 1204 + ] 1205 + 1206 + [[package]] 1133 1207 name = "futures-sink" 1134 1208 version = "0.3.31" 1135 1209 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1149 1223 dependencies = [ 1150 1224 "futures-core", 1151 1225 "futures-io", 1226 + "futures-macro", 1227 + "futures-sink", 1152 1228 "futures-task", 1153 1229 "memchr", 1154 1230 "pin-project-lite", ··· 1157 1233 ] 1158 1234 1159 1235 [[package]] 1236 + name = "generator" 1237 + version = "0.8.7" 1238 + source = "registry+https://github.com/rust-lang/crates.io-index" 1239 + checksum = "605183a538e3e2a9c1038635cc5c2d194e2ee8fd0d1b66b8349fad7dbacce5a2" 1240 + dependencies = [ 1241 + "cc", 1242 + "cfg-if", 1243 + "libc", 1244 + "log", 1245 + "rustversion", 1246 + "windows", 1247 + ] 1248 + 1249 + [[package]] 1160 1250 name = "generic-array" 1161 1251 version = "0.14.7" 1162 1252 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1490 1580 "js-sys", 1491 1581 "log", 1492 1582 "wasm-bindgen", 1493 - "windows-core", 1583 + "windows-core 0.62.1", 1494 1584 ] 1495 1585 1496 1586 [[package]] ··· 1871 1961 "miette", 1872 1962 "multibase", 1873 1963 "multihash", 1964 + "n0-future", 1874 1965 "ouroboros", 1875 1966 "p256", 1876 1967 "rand 0.9.2", ··· 2137 2228 source = "registry+https://github.com/rust-lang/crates.io-index" 2138 2229 checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 2139 2230 dependencies = [ 2140 - "spin", 2231 + "spin 0.9.8", 2141 2232 ] 2142 2233 2143 2234 [[package]] ··· 2195 2286 version = "0.4.28" 2196 2287 source = "registry+https://github.com/rust-lang/crates.io-index" 2197 2288 checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" 2289 + 2290 + [[package]] 2291 + name = "loom" 2292 + version = "0.7.2" 2293 + source = "registry+https://github.com/rust-lang/crates.io-index" 2294 + checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" 2295 + dependencies = [ 2296 + "cfg-if", 2297 + "generator", 2298 + "scoped-tls", 2299 + "tracing", 2300 + "tracing-subscriber", 2301 + ] 2198 2302 2199 2303 [[package]] 2200 2304 name = "lru-cache" ··· 2354 2458 ] 2355 2459 2356 2460 [[package]] 2461 + name = "n0-future" 2462 + version = "0.1.3" 2463 + source = "registry+https://github.com/rust-lang/crates.io-index" 2464 + checksum = "7bb0e5d99e681ab3c938842b96fcb41bf8a7bb4bfdb11ccbd653a7e83e06c794" 2465 + dependencies = [ 2466 + "cfg_aliases", 2467 + "derive_more", 2468 + "futures-buffered", 2469 + "futures-lite", 2470 + "futures-util", 2471 + "js-sys", 2472 + "pin-project", 2473 + "send_wrapper", 2474 + "tokio", 2475 + "tokio-util", 2476 + "wasm-bindgen", 2477 + "wasm-bindgen-futures", 2478 + "web-time", 2479 + ] 2480 + 2481 + [[package]] 2357 2482 name = "ndk-context" 2358 2483 version = "0.1.1" 2359 2484 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2577 2702 ] 2578 2703 2579 2704 [[package]] 2705 + name = "parking" 2706 + version = "2.2.1" 2707 + source = "registry+https://github.com/rust-lang/crates.io-index" 2708 + checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" 2709 + 2710 + [[package]] 2580 2711 name = "parking_lot" 2581 2712 version = "0.12.5" 2582 2713 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2615 2746 checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" 2616 2747 2617 2748 [[package]] 2749 + name = "pin-project" 2750 + version = "1.1.10" 2751 + source = "registry+https://github.com/rust-lang/crates.io-index" 2752 + checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" 2753 + dependencies = [ 2754 + "pin-project-internal", 2755 + ] 2756 + 2757 + [[package]] 2758 + name = "pin-project-internal" 2759 + version = "1.1.10" 2760 + source = "registry+https://github.com/rust-lang/crates.io-index" 2761 + checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" 2762 + dependencies = [ 2763 + "proc-macro2", 2764 + "quote", 2765 + "syn 2.0.106", 2766 + ] 2767 + 2768 + [[package]] 2618 2769 name = "pin-project-lite" 2619 2770 version = "0.2.16" 2620 2771 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3214 3365 ] 3215 3366 3216 3367 [[package]] 3368 + name = "scoped-tls" 3369 + version = "1.0.1" 3370 + source = "registry+https://github.com/rust-lang/crates.io-index" 3371 + checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" 3372 + 3373 + [[package]] 3217 3374 name = "scopeguard" 3218 3375 version = "1.2.0" 3219 3376 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3238 3395 version = "1.0.27" 3239 3396 source = "registry+https://github.com/rust-lang/crates.io-index" 3240 3397 checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" 3398 + 3399 + [[package]] 3400 + name = "send_wrapper" 3401 + version = "0.6.0" 3402 + source = "registry+https://github.com/rust-lang/crates.io-index" 3403 + checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" 3241 3404 3242 3405 [[package]] 3243 3406 name = "serde" ··· 3482 3645 checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 3483 3646 3484 3647 [[package]] 3648 + name = "spin" 3649 + version = "0.10.0" 3650 + source = "registry+https://github.com/rust-lang/crates.io-index" 3651 + checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" 3652 + 3653 + [[package]] 3485 3654 name = "spki" 3486 3655 version = "0.7.3" 3487 3656 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3829 3998 "bytes", 3830 3999 "futures-core", 3831 4000 "futures-sink", 4001 + "futures-util", 3832 4002 "pin-project-lite", 3833 4003 "tokio", 3834 4004 ] ··· 4036 4206 checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" 4037 4207 4038 4208 [[package]] 4209 + name = "unicode-xid" 4210 + version = "0.2.6" 4211 + source = "registry+https://github.com/rust-lang/crates.io-index" 4212 + checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" 4213 + 4214 + [[package]] 4039 4215 name = "unsigned-varint" 4040 4216 version = "0.8.0" 4041 4217 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4267 4443 ] 4268 4444 4269 4445 [[package]] 4446 + name = "windows" 4447 + version = "0.61.3" 4448 + source = "registry+https://github.com/rust-lang/crates.io-index" 4449 + checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" 4450 + dependencies = [ 4451 + "windows-collections", 4452 + "windows-core 0.61.2", 4453 + "windows-future", 4454 + "windows-link 0.1.3", 4455 + "windows-numerics", 4456 + ] 4457 + 4458 + [[package]] 4459 + name = "windows-collections" 4460 + version = "0.2.0" 4461 + source = "registry+https://github.com/rust-lang/crates.io-index" 4462 + checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" 4463 + dependencies = [ 4464 + "windows-core 0.61.2", 4465 + ] 4466 + 4467 + [[package]] 4468 + name = "windows-core" 4469 + version = "0.61.2" 4470 + source = "registry+https://github.com/rust-lang/crates.io-index" 4471 + checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" 4472 + dependencies = [ 4473 + "windows-implement", 4474 + "windows-interface", 4475 + "windows-link 0.1.3", 4476 + "windows-result 0.3.4", 4477 + "windows-strings 0.4.2", 4478 + ] 4479 + 4480 + [[package]] 4270 4481 name = "windows-core" 4271 4482 version = "0.62.1" 4272 4483 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4277 4488 "windows-link 0.2.0", 4278 4489 "windows-result 0.4.0", 4279 4490 "windows-strings 0.5.0", 4491 + ] 4492 + 4493 + [[package]] 4494 + name = "windows-future" 4495 + version = "0.2.1" 4496 + source = "registry+https://github.com/rust-lang/crates.io-index" 4497 + checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" 4498 + dependencies = [ 4499 + "windows-core 0.61.2", 4500 + "windows-link 0.1.3", 4501 + "windows-threading", 4280 4502 ] 4281 4503 4282 4504 [[package]] ··· 4312 4534 version = "0.2.0" 4313 4535 source = "registry+https://github.com/rust-lang/crates.io-index" 4314 4536 checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" 4537 + 4538 + [[package]] 4539 + name = "windows-numerics" 4540 + version = "0.2.0" 4541 + source = "registry+https://github.com/rust-lang/crates.io-index" 4542 + checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" 4543 + dependencies = [ 4544 + "windows-core 0.61.2", 4545 + "windows-link 0.1.3", 4546 + ] 4315 4547 4316 4548 [[package]] 4317 4549 name = "windows-registry" ··· 4466 4698 "windows_x86_64_gnu 0.53.0", 4467 4699 "windows_x86_64_gnullvm 0.53.0", 4468 4700 "windows_x86_64_msvc 0.53.0", 4701 + ] 4702 + 4703 + [[package]] 4704 + name = "windows-threading" 4705 + version = "0.1.0" 4706 + source = "registry+https://github.com/rust-lang/crates.io-index" 4707 + checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" 4708 + dependencies = [ 4709 + "windows-link 0.1.3", 4469 4710 ] 4470 4711 4471 4712 [[package]]
+4
crates/jacquard-common/Cargo.toml
··· 41 41 tracing = { workspace = true, optional = true } 42 42 tokio = { workspace = true, default-features = false, features = ["sync"] } 43 43 44 + # Streaming support (optional) 45 + n0-future = { version = "0.1", optional = true } 46 + 44 47 [target.'cfg(target_family = "wasm")'.dependencies] 45 48 getrandom = { version = "0.3.4", features = ["wasm_js"] } 46 49 ··· 56 59 service-auth = ["crypto-k256", "crypto-p256", "dep:signature"] 57 60 reqwest-client = ["dep:reqwest"] 58 61 tracing = ["dep:tracing"] 62 + streaming = ["n0-future"] 59 63 60 64 [dependencies.ed25519-dalek] 61 65 version = "2"
+3
crates/jacquard-common/src/lib.rs
··· 220 220 pub mod types; 221 221 // XRPC protocol types and traits 222 222 pub mod xrpc; 223 + /// Stream abstractions for HTTP request/response bodies. 224 + #[cfg(feature = "streaming")] 225 + pub mod stream; 223 226 224 227 pub use types::value::*; 225 228
+111
crates/jacquard-common/src/stream.rs
··· 1 + //! Stream abstractions for HTTP request/response bodies 2 + 3 + use std::error::Error; 4 + use std::fmt; 5 + 6 + /// Boxed error type for streaming operations 7 + pub type BoxError = Box<dyn Error + Send + Sync + 'static>; 8 + 9 + /// Error type for streaming operations 10 + #[derive(Debug)] 11 + pub struct StreamError { 12 + kind: StreamErrorKind, 13 + source: Option<BoxError>, 14 + } 15 + 16 + /// Categories of streaming errors 17 + #[derive(Debug, Clone, Copy, PartialEq, Eq)] 18 + pub enum StreamErrorKind { 19 + /// Network or I/O error 20 + Transport, 21 + /// Stream or connection closed 22 + Closed, 23 + /// Protocol violation or framing error 24 + Protocol, 25 + } 26 + 27 + impl StreamError { 28 + /// Create a new streaming error 29 + pub fn new(kind: StreamErrorKind, source: Option<BoxError>) -> Self { 30 + Self { kind, source } 31 + } 32 + 33 + /// Get the error kind 34 + pub fn kind(&self) -> &StreamErrorKind { 35 + &self.kind 36 + } 37 + 38 + /// Get the underlying error source 39 + pub fn source(&self) -> Option<&BoxError> { 40 + self.source.as_ref() 41 + } 42 + 43 + /// Create a "connection closed" error 44 + pub fn closed() -> Self { 45 + Self { 46 + kind: StreamErrorKind::Closed, 47 + source: None, 48 + } 49 + } 50 + 51 + /// Create a transport error with source 52 + pub fn transport(source: impl Error + Send + Sync + 'static) -> Self { 53 + Self { 54 + kind: StreamErrorKind::Transport, 55 + source: Some(Box::new(source)), 56 + } 57 + } 58 + 59 + /// Create a protocol error 60 + pub fn protocol(msg: impl Into<String>) -> Self { 61 + Self { 62 + kind: StreamErrorKind::Protocol, 63 + source: Some(msg.into().into()), 64 + } 65 + } 66 + } 67 + 68 + impl fmt::Display for StreamError { 69 + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 70 + match self.kind { 71 + StreamErrorKind::Transport => write!(f, "Transport error"), 72 + StreamErrorKind::Closed => write!(f, "Stream closed"), 73 + StreamErrorKind::Protocol => write!(f, "Protocol error"), 74 + }?; 75 + 76 + if let Some(source) = &self.source { 77 + write!(f, ": {}", source)?; 78 + } 79 + 80 + Ok(()) 81 + } 82 + } 83 + 84 + impl Error for StreamError { 85 + fn source(&self) -> Option<&(dyn Error + 'static)> { 86 + self.source.as_ref().map(|e| e.as_ref() as &(dyn Error + 'static)) 87 + } 88 + } 89 + 90 + #[cfg(test)] 91 + mod tests { 92 + use super::*; 93 + 94 + #[test] 95 + fn stream_error_carries_kind_and_source() { 96 + let source = std::io::Error::new(std::io::ErrorKind::BrokenPipe, "pipe closed"); 97 + let err = StreamError::new(StreamErrorKind::Transport, Some(Box::new(source))); 98 + 99 + assert_eq!(err.kind(), &StreamErrorKind::Transport); 100 + assert!(err.source().is_some()); 101 + assert_eq!(format!("{}", err), "Transport error: pipe closed"); 102 + } 103 + 104 + #[test] 105 + fn stream_error_without_source() { 106 + let err = StreamError::closed(); 107 + 108 + assert_eq!(err.kind(), &StreamErrorKind::Closed); 109 + assert!(err.source().is_none()); 110 + } 111 + }