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}