sntpc_net_std/
lib.rs

1//! Standard library UDP socket adapter for the [`sntpc`] SNTP client library.
2//!
3//! This crate provides a thin wrapper around [`std::net::UdpSocket`] that implements
4//! the [`NtpUdpSocket`] trait, allowing it to be used with the `sntpc` library for
5//! synchronous SNTP requests.
6//!
7//! # Design Rationale
8//!
9//! The network adapters are separated into their own crates to:
10//! - Allow independent versioning of network implementations
11//! - Minimize dependencies (only `std` and `sntpc` core required)
12//! - Enable users to choose their preferred network stack
13//!
14//! # Example
15//!
16//! ```ignore
17//! use sntpc::{sync::get_time, NtpContext, StdTimestampGen};
18//! use sntpc_net_std::UdpSocketWrapper;
19//! use std::net::UdpSocket;
20//!
21//! let socket = UdpSocket::bind("0.0.0.0:0").expect("Unable to create UDP socket");
22//! socket.set_read_timeout(Some(std::time::Duration::from_secs(2)))
23//!     .expect("Unable to set read timeout");
24//! let socket = UdpSocketWrapper::new(socket);
25//! let context = NtpContext::new(StdTimestampGen::default());
26//!
27//! let result = get_time("pool.ntp.org:123".parse().unwrap(), &socket, context);
28//! match result {
29//!     Ok(time) => println!("Received time: {}.{}", time.sec(), time.sec_fraction()),
30//!     Err(e) => eprintln!("Failed to get time: {:?}", e),
31//! }
32//! ```
33//!
34//! For more examples, see the [repository examples](https://github.com/vpetrigo/sntpc/tree/master/examples).
35
36use sntpc::{Error, NtpUdpSocket, Result};
37
38use std::net::{SocketAddr, UdpSocket};
39
40/// A wrapper around [`std::net::UdpSocket`] that implements [`NtpUdpSocket`].
41///
42/// This type allows standard library UDP sockets to be used with the `sntpc` library
43/// for making SNTP requests. It provides a simple synchronous interface suitable for
44/// blocking I/O operations.
45///
46/// # Example
47///
48/// ```no_run
49/// use sntpc_net_std::UdpSocketWrapper;
50/// use std::net::UdpSocket;
51///
52/// let socket = UdpSocket::bind("0.0.0.0:0").expect("Failed to bind socket");
53/// let wrapper = UdpSocketWrapper::new(socket);
54/// // Use wrapper with sntpc functions
55/// ```
56pub struct UdpSocketWrapper {
57    socket: UdpSocket,
58}
59
60impl UdpSocketWrapper {
61    /// Creates a new `UdpSocketWrapper` from a [`std::net::UdpSocket`].
62    ///
63    /// # Arguments
64    ///
65    /// * `socket` - A UDP socket to wrap
66    ///
67    /// # Example
68    ///
69    /// ```no_run
70    /// use sntpc_net_std::UdpSocketWrapper;
71    /// use std::net::UdpSocket;
72    ///
73    /// let socket = UdpSocket::bind("0.0.0.0:0").expect("Failed to bind");
74    /// let wrapper = UdpSocketWrapper::new(socket);
75    /// ```
76    #[must_use]
77    pub fn new(socket: UdpSocket) -> Self {
78        Self { socket }
79    }
80}
81
82impl From<UdpSocket> for UdpSocketWrapper {
83    /// Converts a [`std::net::UdpSocket`] into a `UdpSocketWrapper`.
84    ///
85    /// This provides a convenient way to create a wrapper using `.into()` or `from()`.
86    ///
87    /// # Example
88    ///
89    /// ```no_run
90    /// use sntpc_net_std::UdpSocketWrapper;
91    /// use std::net::UdpSocket;
92    ///
93    /// let socket = UdpSocket::bind("0.0.0.0:0").expect("Failed to bind");
94    /// let wrapper: UdpSocketWrapper = socket.into();
95    /// ```
96    fn from(socket: UdpSocket) -> Self {
97        UdpSocketWrapper::new(socket)
98    }
99}
100
101impl NtpUdpSocket for UdpSocketWrapper {
102    async fn send_to(&self, buf: &[u8], addr: SocketAddr) -> Result<usize> {
103        match self.socket.send_to(buf, addr) {
104            Ok(usize) => Ok(usize),
105            Err(_) => Err(Error::Network),
106        }
107    }
108
109    async fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, SocketAddr)> {
110        match self.socket.recv_from(buf) {
111            Ok((size, addr)) => Ok((size, addr)),
112            Err(_) => Err(Error::Network),
113        }
114    }
115}