use super::Dht;
use super::bencode::Dict;
use super::krpc::{self, Message};
use std::collections::BTreeMap;
use std::net::SocketAddr;
use std::time::Instant;
#[derive(Clone, Copy, Debug)]
pub(crate) struct Query {
pub(crate) addr: SocketAddr,
pub(crate) door: bool,
deadline: Instant,
}
pub(crate) type Flight = BTreeMap<Vec<u8>, Query>;
impl Dht {
pub(crate) fn ask(
&mut self,
flight: &mut Flight,
addr: SocketAddr,
door: bool,
q: &str,
args: Dict,
) -> bool {
let tid = self.next_tid();
let datagram = krpc::query(&tid, &self.id, q, args);
let sent = self.transport.send(addr, &datagram).is_ok();
if sent {
let deadline = Instant::now() + self.config.deadline;
flight.insert(
tid,
Query {
addr,
door,
deadline,
},
);
}
sent
}
pub(crate) fn land(&mut self, flight: &mut Flight) -> Result<Option<(Query, Message)>, String> {
loop {
let now = Instant::now();
let before = flight.len();
flight.retain(|_, q| q.deadline > now);
let nearest = flight.values().map(|q| q.deadline).min();
let Some(nearest) = nearest.filter(|_| flight.len() == before) else {
return Ok(None);
};
let Some((_, bytes)) = self
.transport
.recv(nearest.saturating_duration_since(now))
.map_err(|e| format!("DHT socket: {e}"))?
else {
continue;
};
let Some(message) = krpc::parse(&bytes) else {
continue;
};
let (Message::Reply { tid, .. } | Message::Error { tid, .. }) = &message;
if let Some(query) = flight.remove(tid) {
return Ok(Some((query, message)));
}
}
}
}