sntpc_net_tokio/
lib.rs

1//! Tokio async runtime UDP socket adapter for the [`sntpc`] SNTP client library.
2//!
3//! This crate provides a wrapper around [`tokio::net::UdpSocket`] that implements
4//! the [`NtpUdpSocket`] trait, enabling asynchronous SNTP requests using the Tokio runtime.
5//!
6//! # Design Rationale
7//!
8//! The network adapters are separated into their own crates to:
9//! - Enable independent versioning (updating Tokio doesn't require updating `sntpc` core)
10//! - Allow version flexibility (works with any Tokio 1.x version)
11//! - Keep the core SNTP protocol logic independent of async runtimes
12//! - Simplify future compatibility (only this adapter needs updating for Tokio 2.x)
13//!
14//! # Example
15//!
16//! ```ignore
17//! use sntpc::{get_time, NtpContext, StdTimestampGen};
18//! use sntpc_net_tokio::UdpSocketWrapper;
19//! use tokio::net::UdpSocket;
20//!
21//! #[tokio::main]
22//! async fn main() {
23//!     let socket = UdpSocket::bind("0.0.0.0:0")
24//!         .await
25//!         .expect("Failed to bind socket");
26//!     let socket = UdpSocketWrapper::from(socket);
27//!     let context = NtpContext::new(StdTimestampGen::default());
28//!
29//!     let result = get_time("pool.ntp.org:123".parse().unwrap(), &socket, context).await;
30//!     match result {
31//!         Ok(time) => println!("Received time: {}.{}", time.sec(), time.sec_fraction()),
32//!         Err(e) => eprintln!("Failed to get time: {:?}", e),
33//!     }
34//! }
35//! ```
36//!
37//! For more examples, see the [repository examples](https://github.com/vpetrigo/sntpc/tree/master/examples/tokio).
38#![no_std]
39
40use sntpc::{Error, NtpUdpSocket, Result};
41use tokio::net::UdpSocket;
42
43use core::net::SocketAddr;
44
45/// A wrapper around [`tokio::net::UdpSocket`] that implements [`NtpUdpSocket`].
46///
47/// This type allows Tokio UDP sockets to be used with the `sntpc` library for making
48/// asynchronous SNTP requests. It integrates seamlessly with Tokio's async runtime
49/// and provides non-blocking I/O operations.
50///
51/// # Example
52///
53/// ```no_run
54/// use sntpc_net_tokio::UdpSocketWrapper;
55/// use tokio::net::UdpSocket;
56///
57/// # async fn example() {
58/// let socket = UdpSocket::bind("0.0.0.0:0").await.expect("Failed to bind socket");
59/// let wrapper = UdpSocketWrapper::new(socket);
60/// // Use wrapper with sntpc async functions
61/// # }
62/// ```
63pub struct UdpSocketWrapper {
64    socket: UdpSocket,
65}
66
67impl UdpSocketWrapper {
68    /// Creates a new `UdpSocketWrapper` from a [`tokio::net::UdpSocket`].
69    ///
70    /// # Arguments
71    ///
72    /// * `socket` - A Tokio UDP socket to wrap
73    ///
74    /// # Example
75    ///
76    /// ```no_run
77    /// use sntpc_net_tokio::UdpSocketWrapper;
78    /// use tokio::net::UdpSocket;
79    ///
80    /// # async fn example() {
81    /// let socket = UdpSocket::bind("0.0.0.0:0").await.expect("Failed to bind");
82    /// let wrapper = UdpSocketWrapper::new(socket);
83    /// # }
84    /// ```
85    #[must_use]
86    pub fn new(socket: UdpSocket) -> Self {
87        Self { socket }
88    }
89}
90
91impl From<UdpSocket> for UdpSocketWrapper {
92    /// Converts a [`tokio::net::UdpSocket`] into a `UdpSocketWrapper`.
93    ///
94    /// This provides a convenient way to create a wrapper using `.into()` or `from()`.
95    ///
96    /// # Example
97    ///
98    /// ```no_run
99    /// use sntpc_net_tokio::UdpSocketWrapper;
100    /// use tokio::net::UdpSocket;
101    ///
102    /// # async fn example() {
103    /// let socket = UdpSocket::bind("0.0.0.0:0").await.expect("Failed to bind");
104    /// let wrapper: UdpSocketWrapper = socket.into();
105    /// # }
106    /// ```
107    fn from(socket: UdpSocket) -> Self {
108        UdpSocketWrapper::new(socket)
109    }
110}
111
112impl NtpUdpSocket for UdpSocketWrapper {
113    async fn send_to(&self, buf: &[u8], addr: SocketAddr) -> Result<usize> {
114        self.socket.send_to(buf, addr).await.map_err(|_| Error::Network)
115    }
116
117    async fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, SocketAddr)> {
118        self.socket.recv_from(buf).await.map_err(|_| Error::Network)
119    }
120}