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}