sntpc 0.9.0

Library for making SNTP requests
Documentation
#[cfg(all(test, feature = "std", feature = "sync"))]
mod sync_tests {
    use sntpc::{Error, NtpContext, NtpUdpSocket, Result, StdTimestampGen, sync::get_time};
    use std::net::{SocketAddr, ToSocketAddrs, UdpSocket};

    struct UdpSocketWrapper {
        socket: UdpSocket,
    }

    impl UdpSocketWrapper {
        #[must_use]
        fn new(socket: UdpSocket) -> Self {
            Self { socket }
        }
    }

    impl From<UdpSocket> for UdpSocketWrapper {
        fn from(socket: UdpSocket) -> Self {
            UdpSocketWrapper::new(socket)
        }
    }

    impl NtpUdpSocket for UdpSocketWrapper {
        async fn send_to(&self, buf: &[u8], addr: SocketAddr) -> Result<usize> {
            match self.socket.send_to(buf, addr) {
                Ok(usize) => Ok(usize),
                Err(_) => Err(Error::Network),
            }
        }

        async fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, SocketAddr)> {
            match self.socket.recv_from(buf) {
                Ok((size, addr)) => Ok((size, addr)),
                Err(_) => Err(Error::Network),
            }
        }
    }

    #[test]
    fn test_ntp_request_sntpv4_supported() {
        let context = NtpContext::new(StdTimestampGen::default());
        let pools = [
            "pool.ntp.org:123",
            "time.google.com:123",
            "time.apple.com:123",
            "time.cloudflare.com:123",
            "time.facebook.com:123",
        ];

        for pool in &pools {
            let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
            socket
                .set_read_timeout(Some(std::time::Duration::from_secs(2)))
                .expect("Unable to set up socket timeout");
            let socket = UdpSocketWrapper::from(socket);

            for address in pool.to_socket_addrs().unwrap().filter(SocketAddr::is_ipv4) {
                let result = get_time(address, &socket, context);

                assert!(result.is_ok(), "{} is bad - {:?}", pool, result.unwrap_err());
                assert_ne!(result.unwrap().seconds, 0);
            }
        }
    }

    #[test]
    fn test_ntp_request_sntpv3_not_supported() {
        let context = NtpContext::new(StdTimestampGen::default());

        let pools = ["time.nist.gov:123", "time.windows.com:123"];

        for pool in &pools {
            let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
            socket
                .set_read_timeout(Some(std::time::Duration::from_secs(5)))
                .expect("Unable to set up socket timeout");
            let socket = UdpSocketWrapper::from(socket);

            for address in pool.to_socket_addrs().unwrap().filter(SocketAddr::is_ipv4) {
                let result = get_time(address, &socket, context);
                assert!(result.is_err(), "{pool} is ok");
                assert_eq!(result.unwrap_err(), Error::IncorrectResponseVersion);
            }
        }
    }

    #[test]
    fn test_invalid_addrs_ntp_request() {
        let pool = "asdf.asdf.asdf:123";
        let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
        socket
            .set_read_timeout(Some(std::time::Duration::from_secs(5)))
            .expect("Unable to set up socket timeout");

        let result = pool.to_socket_addrs();
        assert!(result.is_err(), "{pool} is ok");
    }
}