use crate::bencode::{Bencode, Bytes, dict_get_int};
use crate::error::{Error, ErrorKind};
pub const UT_METADATA_EXT: &str = "ut_metadata";
pub const UT_METADATA_ID: u8 = 2;
pub const METADATA_PIECE_SIZE: u64 = 16 * 1024;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MetadataRequest {
pub piece: u32,
}
impl MetadataRequest {
pub fn to_bencode(&self) -> Bencode {
Bencode::Dict(vec![
(Bytes::from("msg_type"), Bencode::Integer(0)),
(Bytes::from("piece"), Bencode::Integer(self.piece as i64)),
])
}
pub fn from_bencode(val: &Bencode) -> Result<Self, Error> {
let msg_type = dict_get_int(val, b"msg_type")
.ok_or_else(|| Error::new(ErrorKind::PeerInvalidExtendedMessage))?;
if msg_type != 0 {
return Err(Error::new(ErrorKind::PeerInvalidExtendedMessage));
}
let piece = dict_get_int(val, b"piece")
.ok_or_else(|| Error::new(ErrorKind::PeerInvalidExtendedMessage))?;
Ok(MetadataRequest {
piece: piece as u32,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MetadataData {
pub piece: u32,
pub total_size: u64,
pub data: Vec<u8>,
}
impl MetadataData {
pub fn to_bencode_with_data(&self) -> Bencode {
Bencode::Dict(vec![
(Bytes::from("msg_type"), Bencode::Integer(1)),
(Bytes::from("piece"), Bencode::Integer(self.piece as i64)),
(
Bytes::from("total_size"),
Bencode::Integer(self.total_size as i64),
),
])
}
pub fn from_bencode(val: &Bencode, raw_data: Vec<u8>) -> Result<Self, Error> {
let msg_type = dict_get_int(val, b"msg_type")
.ok_or_else(|| Error::new(ErrorKind::PeerInvalidExtendedMessage))?;
if msg_type != 1 {
return Err(Error::new(ErrorKind::PeerInvalidExtendedMessage));
}
let piece = dict_get_int(val, b"piece")
.ok_or_else(|| Error::new(ErrorKind::PeerInvalidExtendedMessage))?;
let total_size = dict_get_int(val, b"total_size")
.ok_or_else(|| Error::new(ErrorKind::PeerInvalidExtendedMessage))?;
Ok(MetadataData {
piece: piece as u32,
total_size: total_size as u64,
data: raw_data,
})
}
pub fn is_reject(val: &Bencode) -> bool {
dict_get_int(val, b"msg_type") == Some(2)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn metadata_request_roundtrip() {
let req = MetadataRequest { piece: 5 };
let ben = req.to_bencode();
let parsed = MetadataRequest::from_bencode(&ben).unwrap();
assert_eq!(parsed.piece, 5);
}
#[test]
fn metadata_data_roundtrip() {
let data = MetadataData {
piece: 2,
total_size: 32768,
data: vec![0x41; 100],
};
let ben = data.to_bencode_with_data();
let parsed = MetadataData::from_bencode(&ben, data.data.clone()).unwrap();
assert_eq!(parsed.piece, 2);
assert_eq!(parsed.total_size, 32768);
assert_eq!(parsed.data.len(), 100);
}
#[test]
fn metadata_reject_detected() {
let ben = Bencode::Dict(vec![
(Bytes::from("msg_type"), Bencode::Integer(2)),
(Bytes::from("piece"), Bencode::Integer(0)),
]);
assert!(MetadataData::is_reject(&ben));
}
#[test]
fn metadata_request_wrong_msg_type() {
let ben = Bencode::Dict(vec![
(Bytes::from("msg_type"), Bencode::Integer(1)),
(Bytes::from("piece"), Bencode::Integer(0)),
]);
assert!(MetadataRequest::from_bencode(&ben).is_err());
}
#[test]
fn metadata_piece_size_constant() {
assert_eq!(METADATA_PIECE_SIZE, 16384);
}
}