use core::convert::TryFrom;
use serde_bytes::Bytes;
use serde_derive::{Deserialize, Serialize};
use crate::{
dht::node::{Id, LocalId},
metainfo::InfoHash,
};
pub const METHOD_ANNOUNCE_PEER: &[u8] = b"announce_peer";
#[derive(Debug, Deserialize, Serialize)]
pub struct QueryArgs<'a> {
#[serde(borrow)]
pub id: &'a Bytes,
#[serde(borrow)]
pub info_hash: &'a Bytes,
#[serde(borrow)]
pub token: &'a Bytes,
#[serde(skip_serializing_if = "Option::is_none")]
pub port: Option<u16>,
#[serde(skip_serializing_if = "Option::is_none")]
pub implied_port: Option<u8>,
}
impl<'a> QueryArgs<'a> {
#[must_use]
#[inline]
pub fn new(
id: &'a LocalId,
info_hash: &'a InfoHash,
token: &'a Bytes,
port: Option<u16>,
implied_port: Option<bool>,
) -> Self {
Self {
id: Bytes::new(&(id.0).0),
info_hash: Bytes::new(&info_hash.0),
token,
port,
implied_port: implied_port.map(u8::from),
}
}
#[must_use]
#[inline]
pub fn id(&self) -> Option<Id> {
Id::try_from(self.id.as_ref()).ok()
}
#[must_use]
#[inline]
pub fn info_hash(&self) -> Option<InfoHash> {
InfoHash::try_from(self.info_hash.as_ref()).ok()
}
#[must_use]
#[inline]
pub fn token(&self) -> &[u8] {
self.token
}
#[must_use]
#[inline]
pub fn implied_port(&self) -> Option<bool> {
self.implied_port.map(|v| v != 0)
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct RespValues<'a> {
#[serde(borrow)]
pub id: &'a Bytes,
}
impl<'a> RespValues<'a> {
#[must_use]
#[inline]
pub fn new(id: &'a LocalId) -> Self {
Self {
id: Bytes::new(&(id.0).0),
}
}
#[must_use]
#[inline]
pub fn id(&self) -> Option<Id> {
Id::try_from(self.id.as_ref()).ok()
}
}
#[cfg(test)]
mod tests {
use serde_bytes::Bytes;
use super::*;
use crate::dht::krpc::{ser, Error, Msg, Ty};
#[test]
fn test_serde_announce_peer_query() -> Result<(), Error> {
let announce_peer_query = b"d1:ad2:id20:abcdefghij01234567899:info_hash20:mnopqrstuvwxyz1234564:porti6331e5:token8:abcd1234e1:q13:announce_peer1:t2:aa1:y1:qe";
let msg: Msg<'_> = bt_bencode::from_slice(announce_peer_query)?;
assert_eq!(msg.tx_id(), b"aa");
assert_eq!(msg.ty(), Ty::Query);
assert_eq!(msg.client_version(), None);
assert_eq!(msg.method_name(), Some(METHOD_ANNOUNCE_PEER));
assert_eq!(
msg.method_name_str(),
Some(core::str::from_utf8(METHOD_ANNOUNCE_PEER).unwrap())
);
let query_args: QueryArgs<'_> = msg.args().unwrap()?;
assert_eq!(query_args.id(), Some(Id::from(*b"abcdefghij0123456789")));
assert_eq!(
query_args.info_hash(),
Some(InfoHash::from(*b"mnopqrstuvwxyz123456"))
);
assert_eq!(query_args.token, b"abcd1234");
assert_eq!(query_args.port, Some(6331));
assert_eq!(query_args.implied_port(), None);
let ser_query_msg = ser::QueryMsg {
t: Bytes::new(b"aa"),
v: None,
q: Bytes::new(METHOD_ANNOUNCE_PEER),
a: query_args,
};
let ser_msg = bt_bencode::to_vec(&ser_query_msg)?;
assert_eq!(ser_msg, announce_peer_query);
Ok(())
}
#[test]
fn test_serde_announce_peer_response_values() -> Result<(), Error> {
let announce_peer_resp = b"d1:rd2:id20:0123456789abcdefghije1:t2:aa1:y1:re";
let msg: Msg<'_> = bt_bencode::from_slice(announce_peer_resp)?;
assert_eq!(msg.tx_id(), b"aa");
assert_eq!(msg.ty(), Ty::Response);
assert_eq!(msg.client_version(), None);
let resp_values: RespValues<'_> = msg.values().unwrap()?;
assert_eq!(resp_values.id(), Some(Id::from(*b"0123456789abcdefghij")));
let ser_resp_msg = ser::RespMsg {
t: Bytes::new(b"aa"),
v: None,
r: &resp_values,
};
let ser_msg = bt_bencode::to_vec(&ser_resp_msg)?;
assert_eq!(ser_msg, announce_peer_resp);
Ok(())
}
}