f1_api/
lib.rs

1//! A Rust implementation of the telemetry API provided by modern F1 video games
2
3use std::io::Error;
4use std::net::SocketAddr;
5
6use socket2::{Domain, Protocol, Socket, Type};
7use tokio::net::UdpSocket;
8use tokio_stream::{Stream, StreamExt};
9use tokio_util::udp::UdpFramed;
10
11use crate::codec::F1Codec;
12use crate::packet::Packet;
13
14pub mod codec;
15pub mod nineteen;
16pub mod packet;
17pub mod types;
18
19/// A high-level interface to the telemetry data of modern F1 video games.
20///
21/// The F1 struct implements a high-level interface to the telemetry data of the
22/// modern F1 video games. It is the recommended way to use the library, as it
23/// provides a simple interface to consumers that hides the low-level internals
24/// of the library.
25pub struct F1 {}
26
27impl F1 {
28    /// Create a stream that yields decoded UDP packets.
29    ///
30    /// Modern F1 games publish their telemetry and session data through a UDP-based protocol. With
31    /// this function, a stream can be created that listens at the given socket for incoming
32    /// packets, decodes them using the `F1Codec`, and returns their Rust representations.
33    ///
34    /// # Examples
35    ///
36    /// ```
37    /// use std::net::{IpAddr, SocketAddr};
38    ///
39    /// use f1_api::F1;
40    /// use f1_api::packet::Packet::{Event, Lap, Motion, Participants, Session, Setup, Status, Telemetry};
41    /// use tokio_stream::StreamExt;
42    ///
43    /// async fn example() {
44    ///     let ip_address = IpAddr::from([0, 0, 0, 0]);
45    ///     let port = 20777;
46    ///     let socket = SocketAddr::new(ip_address, port);
47    ///
48    ///     let mut stream = F1::stream(socket).unwrap();
49    ///
50    ///     while let Some(packet) = stream.next().await {
51    ///         match packet {
52    ///             Event(_) => println!("Received Event packet"),
53    ///             Lap(_) => println!("Received Lap packet"),
54    ///             Motion(_) => println!("Received Motion packet"),
55    ///             Participants(_) => println!("Received Participants packet"),
56    ///             Session(_) => println!("Received Session packet"),
57    ///             Setup(_) => println!("Received Setup packet"),
58    ///             Status(_) => println!("Received Status packet"),
59    ///             Telemetry(_) => println!("Received Telemetry packet"),
60    ///         }
61    ///     }
62    /// }
63    /// ```
64    pub fn stream(socket_address: SocketAddr) -> Result<impl Stream<Item = Packet>, Error> {
65        let socket = match socket_address {
66            SocketAddr::V4(_) => Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::UDP)),
67            SocketAddr::V6(_) => Socket::new(Domain::IPV6, Type::DGRAM, Some(Protocol::UDP)),
68        }?;
69
70        socket.bind(&socket_address.into())?;
71
72        Ok(UdpFramed::new(UdpSocket::from_std(socket.into())?, F1Codec)
73            .map(|result| result.unwrap())
74            .map(|(packet, _address)| packet))
75    }
76}