Skip to main content

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}