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}