structfs_handles/lib.rs
1//! # structfs-handles
2//!
3//! Handle-store and streaming primitives for StructFS.
4//!
5//! The deferred-operation pattern — write a request, get an
6//! `outstanding/{id}` handle path back, read the handle for results — is
7//! the backbone of every broker-shaped StructFS store. This crate makes it
8//! a primitive instead of a convention:
9//!
10//! - [`HandleStore`] + [`HandleProtocol`]: generic `outstanding/{id}`
11//! scaffolding — id minting, routing, the no-overwrite rule, Null-write
12//! release with cancellation, listing.
13//! - [`TailLog`] / [`TailPage`]: append-only event streams with **atomic
14//! tail reads** — items and terminal status in one operation, so the
15//! "close-out drain" race cannot exist.
16//! - [`Gate`] / [`CancelToken`]: park-until-predicate with the
17//! enable-before-check ordering baked in (no lost wakeups), and
18//! cancellation that fails parked reads while leaving writes open.
19//! - [`SyncBridge`]: run a detached async store from synchronous code on a
20//! blocking thread.
21//! - [`conformance`]: certify any handle store against the protocol rules.
22
23mod duplex;
24pub use duplex::{DuplexStream, StreamReadiness, StreamStore};
25mod byte_stream;
26mod gate;
27mod handle_store;
28mod sync_bridge;
29mod tail;
30
31pub mod conformance;
32
33pub use byte_stream::{ByteChunk, ByteStream};
34pub use gate::{CancelToken, Cancelled, Gate};
35pub use handle_store::{HandleCx, HandleProtocol, HandleStore};
36pub use sync_bridge::SyncBridge;
37pub use tail::{TailLog, TailPage};
38
39// Re-export the async trait surface these types implement.
40pub use structfs_core_store::{
41 DetachedFuture, DetachedReader, DetachedStore, DetachedWriter, Error, Path, Record, Value,
42};
43
44#[cfg(test)]
45mod tests {
46 use super::*;
47 use std::sync::Arc;
48
49 struct NullProtocol;
50
51 impl HandleProtocol for NullProtocol {
52 type Handle = Value;
53
54 fn open(&self, _cx: HandleCx, request: Value) -> Result<Self::Handle, Error> {
55 Ok(request)
56 }
57
58 fn read(&self, handle: Arc<Self::Handle>, _sub: Path) -> DetachedFuture<Option<Record>> {
59 Box::pin(async move { Ok(Some(Record::parsed((*handle).clone()))) })
60 }
61
62 fn write(
63 &self,
64 _handle: Arc<Self::Handle>,
65 sub: Path,
66 _data: Record,
67 ) -> DetachedFuture<Path> {
68 Box::pin(async move { Ok(sub) })
69 }
70 }
71
72 #[tokio::test]
73 async fn handle_store_passes_conformance() {
74 let mut store = HandleStore::new(NullProtocol);
75 conformance::check_handle_conventions(&mut store, Value::from("request")).await;
76 }
77}