localsnd 0.5.21

CLI implementation of localsend
Documentation
use std::{
	net::{Ipv4Addr, SocketAddr},
	time::{Duration, Instant},
};

use crate::localsend_proto::{
	Device, DeviceType,
	dto::{MulticastDto, RegisterDto},
};
use tokio::net::UdpSocket;

#[derive(Debug)]
pub struct MulticastDeviceScanner {
	socket: UdpSocket,
	device: MulticastDto,
	addr: SocketAddr,
	announce_msg: String,
}

impl MulticastDeviceScanner {
	pub async fn new(device: &Device, multicast: Ipv4Addr, port: u16, http_port: u16) -> std::io::Result<Self> {
		let socket = UdpSocket::bind((Ipv4Addr::UNSPECIFIED, port)).await?;
		socket.join_multicast_v4(multicast, Ipv4Addr::UNSPECIFIED)?;

		let device = MulticastDto::v2(device.alias.clone(), device.device_model.clone(), DeviceType::Headless, device.fingerprint.clone(), http_port, true);
		let announce_msg = serde_json::to_string(&device)?;

		Ok(Self { socket, device, addr: (multicast, port).into(), announce_msg })
	}
}

impl MulticastDeviceScanner {
	pub async fn send_announcement(&self) {
		let size = self.socket.send_to(self.announce_msg.as_bytes(), self.addr).await.ok();
		assert!(size == Some(self.announce_msg.len()));
	}

	pub async fn scan(&self) -> std::io::Result<Vec<Device>> {
		let mut devices = vec![];
		let mut buf = [0u8; 2048_usize];

		self.send_announcement().await;

		let instant = Instant::now();
		while instant.elapsed() < Duration::from_secs(2) || devices.is_empty() {
			if let Ok((size, addr)) = self.socket.try_recv_from(&mut buf) {
				let register_dto: RegisterDto = serde_json::from_slice(&buf[..size])?;
				if register_dto.fingerprint == self.device.fingerprint {
					continue;
				}

				let device = register_dto.mark_device(addr.ip().to_string(), addr.port(), false);
				if !devices.contains(&device) {
					log::trace!("found device: {:?}", device);
					devices.push(device);
				}
			} else {
				tokio::time::sleep(Duration::from_millis(100)).await;
			}
		}
		Ok(devices)
	}
}