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}