use core::convert::TryFrom;
use crate::dht::node::AddrOptId;
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::vec::Vec;
use bt_bencode::Value;
#[cfg(feature = "std")]
use std::vec::Vec;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct Id(pub [u8; 2]);
impl Id {
pub fn rand<R>(rng: &mut R) -> Result<Self, rand::Error>
where
R: rand::Rng,
{
let mut inner = [0u8; 2];
rng.try_fill(&mut inner)?;
Ok(Self(inner))
}
}
impl From<u16> for Id {
fn from(value: u16) -> Self {
Self(value.to_be_bytes())
}
}
impl TryFrom<&[u8]> for Id {
type Error = core::array::TryFromSliceError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
<[u8; core::mem::size_of::<u16>()]>::try_from(value).map(Id)
}
}
impl AsRef<[u8]> for Id {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
#[derive(Debug)]
pub struct Transaction<Addr, TxId, Instant> {
pub addr_opt_id: AddrOptId<Addr>,
pub tx_id: TxId,
pub method: &'static [u8],
pub timeout_deadline: Instant,
}
#[cfg(feature = "std")]
impl<Addr, TxId, Instant> std::hash::Hash for Transaction<Addr, TxId, Instant>
where
TxId: std::hash::Hash,
Addr: std::hash::Hash,
{
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.tx_id.hash(state);
self.addr_opt_id.hash(state);
}
}
impl<Addr, TxId, Instant> Transaction<Addr, TxId, Instant> {
#[must_use]
#[inline]
pub const fn new(
addr_opt_id: AddrOptId<Addr>,
tx_id: TxId,
method: &'static [u8],
timeout_deadline: Instant,
) -> Self {
Self {
addr_opt_id,
tx_id,
method,
timeout_deadline,
}
}
#[must_use]
#[inline]
pub const fn addr_opt_id(&self) -> &AddrOptId<Addr> {
&self.addr_opt_id
}
#[must_use]
#[inline]
pub const fn tx_id(&self) -> &TxId {
&self.tx_id
}
#[must_use]
#[inline]
pub const fn timeout_deadline(&self) -> &Instant {
&self.timeout_deadline
}
}
#[cfg_attr(feature = "std", derive(thiserror::Error))]
#[cfg_attr(feature = "std", error(transparent))]
#[derive(Debug)]
pub struct Error {
kind: ErrorKind,
}
impl Error {
#[must_use]
#[inline]
const fn unknown_tx() -> Self {
Self {
kind: ErrorKind::UnknownTx,
}
}
#[must_use]
#[inline]
pub const fn is_unknown_tx(&self) -> bool {
matches!(self.kind, ErrorKind::UnknownTx)
}
}
#[cfg(feature = "std")]
impl From<Error> for std::io::Error {
fn from(error: Error) -> Self {
match error.kind {
ErrorKind::UnknownTx => std::io::Error::new(std::io::ErrorKind::Other, error),
}
}
}
#[cfg_attr(feature = "std", derive(thiserror::Error))]
#[derive(Debug)]
enum ErrorKind {
#[cfg_attr(feature = "std", error("unknown transaction"))]
UnknownTx,
}
#[derive(Clone, Debug)]
pub struct ReadEvent<Addr, TxId> {
addr_opt_id: AddrOptId<Addr>,
tx_id: Option<TxId>,
msg: Value,
}
impl<Addr, TxId> ReadEvent<Addr, TxId> {
#[must_use]
#[inline]
pub const fn addr_opt_id(&self) -> &AddrOptId<Addr> {
&self.addr_opt_id
}
#[must_use]
#[inline]
pub const fn tx_id(&self) -> Option<&TxId> {
self.tx_id.as_ref()
}
#[must_use]
#[inline]
pub const fn msg(&self) -> &Value {
&self.msg
}
}
#[derive(Debug)]
pub struct Transactions<Addr, TxId, Instant> {
txs: Vec<Transaction<Addr, TxId, Instant>>,
}
impl<Addr, TxId, Instant> Default for Transactions<Addr, TxId, Instant> {
fn default() -> Self {
Self::new()
}
}
impl<Addr, TxId, Instant> Transactions<Addr, TxId, Instant> {
#[must_use]
#[inline]
pub const fn new() -> Self {
Self { txs: Vec::new() }
}
#[inline]
pub fn insert(&mut self, tx: Transaction<Addr, TxId, Instant>)
where
TxId: PartialEq,
{
assert!(!self.contains(&tx.tx_id));
self.txs.push(tx);
}
#[inline]
pub fn remove(&mut self, tx_id: &TxId) -> Option<Transaction<Addr, TxId, Instant>>
where
TxId: PartialEq,
{
self.txs
.iter()
.position(|t| t.tx_id == *tx_id)
.map(|index| self.txs.remove(index))
}
#[must_use]
#[inline]
pub fn contains(&self, tx_id: &TxId) -> bool
where
TxId: PartialEq,
{
self.txs.iter().any(|tx| tx.tx_id == *tx_id)
}
#[must_use]
#[inline]
pub fn len(&self) -> usize {
self.txs.len()
}
#[must_use]
#[inline]
pub fn is_empty(&self) -> bool {
self.txs.is_empty()
}
#[must_use]
#[inline]
pub fn timeout(&self) -> Option<Instant>
where
Instant: crate::time::Instant,
{
self.txs.iter().map(|t| &t.timeout_deadline).min().cloned()
}
#[inline]
pub fn pop_timed_out_tx(&mut self, now: &Instant) -> Option<Transaction<Addr, TxId, Instant>>
where
Instant: crate::time::Instant,
{
if let Some(pos) = self.txs.iter().position(|tx| tx.timeout_deadline <= *now) {
return Some(self.txs.remove(pos));
}
None
}
pub fn on_recv(
&mut self,
tx_id: &TxId,
) -> Result<Transaction<Addr, TxId, Instant>, Error>
where
Addr: PartialEq,
TxId: PartialEq + for<'a> TryFrom<&'a [u8]>,
{
self.remove(tx_id).ok_or_else(Error::unknown_tx)
}
}