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
//! A Rust implementation of the telemetry API provided by modern F1 video games

use crate::codec::F1Codec;
use crate::packet::Packet;
use net2::UdpBuilder;
use std::io::Error;
use std::net::SocketAddr;
use tokio::net::UdpSocket;
use tokio::stream::{Stream, StreamExt};
use tokio_util::udp::UdpFramed;

pub mod codec;
pub mod nineteen;
pub mod packet;
pub mod types;

/// A high-level interface to the telemetry data of modern F1 video games.
///
/// The F1 struct implements a high-level interface to the telemetry data of the
/// modern F1 video games. It is the recommended way to use the library, as it
/// provides a simple interface to consumers that hides the low-level internals
/// of the library.
pub struct F1 {}

impl F1 {
    /// Create a stream that yields decoded UDP packets.
    ///
    /// Modern F1 games publish their telemetry and session data through a UDP-based protocol. With
    /// this function, a stream can be created that listens at the given socket for incoming
    /// packets, decodes them using the `F1Codec`, and returns their Rust representations.
    ///
    /// # Examples
    ///
    /// ```
    /// use f1_api::F1;
    /// use f1_api::packet::Packet::{Event, Lap, Motion, Participants, Session, Setup, Status, Telemetry};
    /// use std::net::{IpAddr, SocketAddr};
    /// use tokio::stream::StreamExt;
    ///
    /// async fn example() {
    ///     let ip_address = IpAddr::from([0, 0, 0, 0]);
    ///     let port = 20777;
    ///     let socket = SocketAddr::new(ip_address, port);
    ///
    ///     let mut stream = F1::stream(socket).unwrap();
    ///
    ///     while let Some(packet) = stream.next().await {
    ///         match packet {
    ///             Event(_) => println!("Received Event packet"),
    ///             Lap(_) => println!("Received Lap packet"),
    ///             Motion(_) => println!("Received Motion packet"),
    ///             Participants(_) => println!("Received Participants packet"),
    ///             Session(_) => println!("Received Session packet"),
    ///             Setup(_) => println!("Received Setup packet"),
    ///             Status(_) => println!("Received Status packet"),
    ///             Telemetry(_) => println!("Received Telemetry packet"),
    ///         }
    ///     }
    /// }
    /// ```
    pub fn stream(socket_address: SocketAddr) -> Result<impl Stream<Item = Packet>, Error> {
        let socket = match socket_address {
            SocketAddr::V4(address) => UdpBuilder::new_v4()?.bind(address),
            SocketAddr::V6(address) => UdpBuilder::new_v6()?.only_v6(true)?.bind(address),
        }?;

        Ok(UdpFramed::new(UdpSocket::from_std(socket)?, F1Codec)
            .map(|result| result.unwrap())
            .map(|(packet, _address)| packet))
    }
}