1use std::{net::SocketAddr, pin::Pin, time::Duration};
2
3use crate::{
4 header::{Header, HeaderIdentifier},
5 io::{DecodeFrom, EncodeTo},
6 packet::{Packet, PacketCategory, Status},
7 Result,
8};
9use tokio::{
10 io::AsyncWriteExt,
11 io::{BufReader, BufWriter},
12 net::{TcpStream, ToSocketAddrs, UdpSocket},
13};
14
15const HELLO_PACKET: [u8; 16] = [
16 b'E', b'S', b'C', b'/', b'V', b'P', b'.', b'n', b'e', b't', 0x10, 1, 0, 0, 0, 0, ];
23const BUF_SIZE: usize = 1024;
24
25const CONNECT_PACKET: Packet = Packet {
26 category: PacketCategory::Connect,
27 status: Status::Null,
28 headers: vec![],
29};
30
31pub struct Client {
32 stream: TcpStream,
33}
34
35impl Client {
36 pub async fn discover<A: ToSocketAddrs>(
37 bind_addr: A,
38 broadcast_addr: A,
39 timeout: Duration,
40 ) -> Result<Vec<Projector>> {
41 let socket = UdpSocket::bind(bind_addr).await?;
42
43 socket.set_broadcast(true)?;
44
45 socket.send_to(&HELLO_PACKET, broadcast_addr).await?;
46 let mut projectors = Vec::new();
47 let mut buf = [0; BUF_SIZE];
48
49 while let Ok((n, addr)) =
50 match tokio::time::timeout(timeout, socket.recv_from(&mut buf)).await {
51 Ok(result) => result,
52 Err(_) => return Ok(projectors),
53 }
54 {
55 let packet = Packet::decode_from(&mut Pin::new(&mut &buf[..n])).await?; let name = packet
57 .headers
58 .iter()
59 .find(|h| matches!(h.identifier(), HeaderIdentifier::ProjectorName))
60 .map(|h| h.information().to_string());
61 projectors.push(Projector { addr, name })
62 }
63 Ok(projectors)
64 }
65
66 pub async fn connect<A: ToSocketAddrs>(
67 addr: A,
68 password: Option<String>,
69 timeout: Duration,
70 ) -> Result<Self> {
71 let mut stream = tokio::time::timeout(timeout, TcpStream::connect(addr))
72 .await
73 .map_err(|_| {
74 crate::Error::new(
75 crate::error::ErrorKind::IO(std::io::ErrorKind::TimedOut),
76 "Timed out".to_string(),
77 )
78 })??;
79 {
80 let (reader, writer) = stream.split();
81 let mut writer = BufWriter::new(writer);
82 let mut pinned_writer = Pin::new(&mut writer);
83 let mut reader = BufReader::new(reader);
84 let mut pinned_reader = Pin::new(&mut reader);
85
86 let packet = match password {
87 None => CONNECT_PACKET,
88 Some(password) => {
89 let mut packet = CONNECT_PACKET;
90 packet
91 .headers
92 .push(Header::new(HeaderIdentifier::Password, 1, password)?);
93 packet
94 }
95 };
96
97 packet.encode_to(&mut pinned_writer).await?;
98 writer.flush().await?;
99 Packet::decode_from(&mut pinned_reader)
100 .await?
101 .status_as_result()?;
102 }
103 Ok(Self { stream })
104 }
105
106 pub async fn send_packet(&mut self, packet: Packet) -> Result<Packet> {
107 let (reader, writer) = self.stream.split();
108 let mut writer = BufWriter::new(writer);
109 let mut pinned_writer = Pin::new(&mut writer);
110 let mut reader = BufReader::new(reader);
111 let mut pinned_reader = Pin::new(&mut reader);
112 packet.encode_to(&mut pinned_writer).await?;
113
114 Packet::decode_from(&mut pinned_reader).await
115 }
116}
117
118pub struct Projector {
119 addr: SocketAddr,
120 name: Option<String>,
121}
122
123impl Projector {
124 pub fn addr(&self) -> SocketAddr {
125 self.addr
126 }
127 pub fn name(&self) -> Option<String> {
128 self.name.clone()
129 }
130}