ndn_ipc/blocking.rs
1//! Synchronous (blocking) wrapper for [`ForwarderClient`].
2//!
3//! Useful for non-async contexts such as C FFI or Python bindings where
4//! spawning a Tokio runtime manually is more ergonomic than using `async/await`.
5//!
6//! # Example
7//!
8//! ```rust,no_run
9//! use ndn_ipc::BlockingForwarderClient;
10//! use ndn_packet::Name;
11//!
12//! let mut client = BlockingForwarderClient::connect("/run/nfd/nfd.sock").unwrap();
13//! let prefix: Name = "/example".parse().unwrap();
14//! client.register_prefix(&prefix).unwrap();
15//!
16//! // Send a raw NDN packet.
17//! client.send(bytes::Bytes::from_static(b"\x05\x01\x00")).unwrap();
18//!
19//! // Receive a raw NDN packet.
20//! if let Some(pkt) = client.recv() {
21//! println!("received {} bytes", pkt.len());
22//! }
23//! ```
24
25use std::path::Path;
26
27use bytes::Bytes;
28use tokio::runtime::Runtime;
29
30use ndn_packet::Name;
31
32use crate::forwarder_client::{ForwarderClient, ForwarderError};
33
34/// Synchronous (blocking) client for communicating with a running `ndn-fwd`.
35///
36/// Wraps [`ForwarderClient`] with a private Tokio runtime so callers do not
37/// need to manage an async runtime. All methods block the calling thread.
38pub struct BlockingForwarderClient {
39 rt: Runtime,
40 inner: ForwarderClient,
41}
42
43impl BlockingForwarderClient {
44 /// Connect to the forwarder's face socket (blocking).
45 ///
46 /// Automatically attempts SHM data plane; falls back to Unix socket.
47 ///
48 /// # Errors
49 ///
50 /// Returns [`ForwarderError`] if the socket is unreachable or the
51 /// connection handshake fails.
52 pub fn connect(face_socket: impl AsRef<Path>) -> Result<Self, ForwarderError> {
53 let rt = tokio::runtime::Builder::new_current_thread()
54 .enable_all()
55 .build()
56 .map_err(ForwarderError::Io)?;
57 let inner = rt.block_on(ForwarderClient::connect(face_socket))?;
58 Ok(Self { rt, inner })
59 }
60
61 /// Connect using only the Unix socket for data (no SHM attempt).
62 pub fn connect_unix_only(face_socket: impl AsRef<Path>) -> Result<Self, ForwarderError> {
63 let rt = tokio::runtime::Builder::new_current_thread()
64 .enable_all()
65 .build()
66 .map_err(ForwarderError::Io)?;
67 let inner = rt.block_on(ForwarderClient::connect_unix_only(face_socket))?;
68 Ok(Self { rt, inner })
69 }
70
71 /// Send a raw NDN packet (blocking).
72 pub fn send(&self, pkt: Bytes) -> Result<(), ForwarderError> {
73 self.rt.block_on(self.inner.send(pkt))
74 }
75
76 /// Receive a raw NDN packet (blocking).
77 ///
78 /// Returns `None` if the forwarder connection is closed.
79 pub fn recv(&self) -> Option<Bytes> {
80 self.rt.block_on(self.inner.recv())
81 }
82
83 /// Register a prefix with the forwarder (blocking).
84 pub fn register_prefix(&self, prefix: &Name) -> Result<(), ForwarderError> {
85 self.rt.block_on(self.inner.register_prefix(prefix))
86 }
87
88 /// Unregister a prefix from the forwarder (blocking).
89 pub fn unregister_prefix(&self, prefix: &Name) -> Result<(), ForwarderError> {
90 self.rt.block_on(self.inner.unregister_prefix(prefix))
91 }
92
93 /// Whether this client is using SHM for data transport.
94 pub fn is_shm(&self) -> bool {
95 self.inner.is_shm()
96 }
97
98 /// Whether the forwarder connection has been lost.
99 pub fn is_dead(&self) -> bool {
100 self.inner.is_dead()
101 }
102
103 /// Gracefully tear down the client (blocking).
104 pub fn close(self) {
105 let Self { rt, inner } = self;
106 rt.block_on(inner.close());
107 }
108}