1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
//! A full implementation of the Ether Dream laser protocol.

#[macro_use]
extern crate bitflags;
extern crate byteorder;

pub mod dac;
pub mod protocol;

use protocol::{ReadBytes, SizeBytes};
use std::{io, net};

/// An iterator that listens and waits for broadcast messages from DACs on the network and yields
/// them as they are received on the inner UDP socket.
///
/// Yields an `io::Error` if:
///
/// - An error occurred when receiving on the inner UDP socket.
/// - A `DacBroadcast` could not be read from the received bytes.
pub struct RecvDacBroadcasts {
    udp_socket: net::UdpSocket,
    buffer: [u8; RecvDacBroadcasts::BUFFER_LEN],
}

impl RecvDacBroadcasts {
    /// The size of the inner buffer used to receive broadcast messages.
    pub const BUFFER_LEN: usize = protocol::DacBroadcast::SIZE_BYTES;
}

/// Produces a `RecvDacBroadcasts` instance that listens and waits for broadcast messages from DACs
/// on the network and yields them as they are received on the inner UDP socket.
///
/// This function returns an `io::Error` if it could not bind to the broadcast address
/// `255.255.255.255:<protocol::BROADCAST_PORT>`.
///
/// The produced iterator yields an `io::Error` if:
///
/// - An error occurred when receiving on the inner UDP socket. This may include `TimedOut` or
/// `WouldBlock` if either of the `set_timeout` or `set_nonblocking` methods have been called.
/// - A `DacBroadcast` could not be read from the received bytes.
///
/// ## Example
///
/// ```no_run
/// extern crate ether_dream;
///
/// fn main() {
///     let dac_broadcasts = ether_dream::recv_dac_broadcasts().expect("failed to bind to UDP socket");
///     for dac_broadcast in dac_broadcasts {
///         println!("{:#?}", dac_broadcast);
///     }
/// }
/// ```
pub fn recv_dac_broadcasts() -> io::Result<RecvDacBroadcasts> {
    let broadcast_port = protocol::BROADCAST_PORT;
    let broadcast_addr = net::SocketAddrV4::new([0, 0, 0, 0].into(), broadcast_port);
    let udp_socket = net::UdpSocket::bind(broadcast_addr)?;
    let buffer = [0; RecvDacBroadcasts::BUFFER_LEN];
    Ok(RecvDacBroadcasts { udp_socket, buffer })
}

impl RecvDacBroadcasts {
    /// Attempt to read the next broadcast.
    ///
    /// This method may or may not block depending on whether `set_nonblocking` has been called. By
    /// default, this method will block.
    pub fn next_broadcast(&mut self) -> io::Result<(protocol::DacBroadcast, net::SocketAddr)> {
        let RecvDacBroadcasts {
            ref mut buffer,
            ref mut udp_socket,
        } = *self;
        let (_len, src_addr) = udp_socket.recv_from(buffer)?;
        let mut bytes = &buffer[..];
        let dac_broadcast = bytes.read_bytes::<protocol::DacBroadcast>()?;
        Ok((dac_broadcast, src_addr))
    }

    /// Set the timeout for the inner UDP socket used for reading broadcasts.
    ///
    /// See the [`std::net::UdpSocket::set_read_timeout`
    /// docs](https://doc.rust-lang.org/std/net/struct.UdpSocket.html#method.set_read_timeout) for
    /// details.
    pub fn set_timeout(&self, duration: Option<std::time::Duration>) -> io::Result<()> {
        self.udp_socket.set_read_timeout(duration)
    }

    /// Moves the inner UDP socket into or out of nonblocking mode.
    ///
    /// See the
    /// [`std::net::UdpSocket::set_nonblocking`](https://doc.rust-lang.org/std/net/struct.UdpSocket.html#method.set_nonblocking)
    /// for details.
    pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
        self.udp_socket.set_nonblocking(nonblocking)
    }
}

impl Iterator for RecvDacBroadcasts {
    type Item = io::Result<(protocol::DacBroadcast, net::SocketAddr)>;
    fn next(&mut self) -> Option<Self::Item> {
        Some(self.next_broadcast())
    }
}