Skip to main content

structfs_handles/
sync_bridge.rs

1//! Sync facade over detached async stores.
2
3use structfs_core_store::{DetachedStore, Error, Path, Reader, Record, Writer};
4
5/// Adapts a detached async store to the synchronous `Reader`/`Writer`
6/// traits by blocking on a tokio runtime handle.
7///
8/// # Contract
9///
10/// Operations must be called from a thread that is NOT a tokio runtime
11/// worker (`Handle::block_on` panics inside a runtime context). The
12/// intended callers are dedicated blocking threads — `spawn_blocking`
13/// closures, or plain OS threads — which is exactly where synchronous
14/// block code runs.
15pub struct SyncBridge<S> {
16    inner: S,
17    runtime: tokio::runtime::Handle,
18}
19
20impl<S: DetachedStore> SyncBridge<S> {
21    /// Bridge a detached store onto a runtime handle.
22    pub fn new(inner: S, runtime: tokio::runtime::Handle) -> Self {
23        Self { inner, runtime }
24    }
25
26    /// Unwrap, returning the inner store.
27    pub fn into_inner(self) -> S {
28        self.inner
29    }
30}
31
32impl<S: DetachedStore + Sync> Reader for SyncBridge<S> {
33    fn read(&mut self, from: &Path) -> Result<Option<Record>, Error> {
34        let fut = self.inner.read_detached(from);
35        self.runtime.block_on(fut)
36    }
37}
38
39impl<S: DetachedStore + Sync> Writer for SyncBridge<S> {
40    fn write(&mut self, to: &Path, data: Record) -> Result<Path, Error> {
41        let fut = self.inner.write_detached(to, data);
42        self.runtime.block_on(fut)
43    }
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49    use structfs_core_store::{path, MemoryStore, Shared, Value};
50
51    #[test]
52    fn bridge_runs_on_plain_thread() {
53        let runtime = tokio::runtime::Runtime::new().unwrap();
54        let store = Shared::new(MemoryStore::new());
55        let handle = runtime.handle().clone();
56
57        let worker = std::thread::spawn(move || {
58            let mut bridge = SyncBridge::new(store, handle);
59            bridge
60                .write(&path!("key"), Record::parsed(Value::from("v")))
61                .unwrap();
62            bridge.read(&path!("key")).unwrap().is_some()
63        });
64        assert!(worker.join().unwrap());
65    }
66}