use std::io::{self, Write};
use byteorder::{BigEndian, WriteBytesExt};
use nom::{be_u32, IResult, be_u64};
use announce::AnnounceResponse;
use contact::CompactPeers;
use error::ErrorResponse;
use scrape::ScrapeResponse;
const ERROR_ACTION_ID: u32 = 3;
pub enum ResponseType<'a> {
Connect(u64),
Announce(AnnounceResponse<'a>),
Scrape(ScrapeResponse<'a>),
Error(ErrorResponse<'a>),
}
impl<'a> ResponseType<'a> {
pub fn to_owned(&self) -> ResponseType<'static> {
match self {
&ResponseType::Connect(id) => ResponseType::Connect(id),
&ResponseType::Announce(ref res) => ResponseType::Announce(res.to_owned()),
&ResponseType::Scrape(ref res) => ResponseType::Scrape(res.to_owned()),
&ResponseType::Error(ref err) => ResponseType::Error(err.to_owned()),
}
}
}
pub struct TrackerResponse<'a> {
transaction_id: u32,
response_type: ResponseType<'a>,
}
impl<'a> TrackerResponse<'a> {
pub fn new(trans_id: u32, res_type: ResponseType<'a>) -> TrackerResponse<'a> {
TrackerResponse {
transaction_id: trans_id,
response_type: res_type,
}
}
pub fn from_bytes(bytes: &'a [u8]) -> IResult<&'a [u8], TrackerResponse<'a>> {
parse_response(bytes)
}
pub fn write_bytes<W>(&self, mut writer: W) -> io::Result<()>
where W: Write
{
match self.response_type() {
&ResponseType::Connect(id) => {
try!(writer.write_u32::<BigEndian>(::CONNECT_ACTION_ID));
try!(writer.write_u32::<BigEndian>(self.transaction_id()));
try!(writer.write_u64::<BigEndian>(id));
}
&ResponseType::Announce(ref req) => {
let action_id = match req.peers() {
&CompactPeers::V4(_) => ::ANNOUNCE_IPV4_ACTION_ID,
&CompactPeers::V6(_) => ::ANNOUNCE_IPV6_ACTION_ID,
};
try!(writer.write_u32::<BigEndian>(action_id));
try!(writer.write_u32::<BigEndian>(self.transaction_id()));
try!(req.write_bytes(writer));
}
&ResponseType::Scrape(ref req) => {
try!(writer.write_u32::<BigEndian>(::SCRAPE_ACTION_ID));
try!(writer.write_u32::<BigEndian>(self.transaction_id()));
try!(req.write_bytes(writer));
}
&ResponseType::Error(ref err) => {
try!(writer.write_u32::<BigEndian>(ERROR_ACTION_ID));
try!(writer.write_u32::<BigEndian>(self.transaction_id()));
try!(err.write_bytes(writer));
}
};
Ok(())
}
pub fn transaction_id(&self) -> u32 {
self.transaction_id
}
pub fn response_type(&self) -> &ResponseType<'a> {
&self.response_type
}
pub fn to_owned(&self) -> TrackerResponse<'static> {
TrackerResponse {
transaction_id: self.transaction_id,
response_type: self.response_type().to_owned(),
}
}
}
fn parse_response<'a>(bytes: &'a [u8]) -> IResult<&'a [u8], TrackerResponse<'a>> {
switch!(bytes, tuple!(be_u32, be_u32),
(::CONNECT_ACTION_ID, tid) => map!(be_u64, |cid| TrackerResponse::new(tid, ResponseType::Connect(cid)) ) |
(::ANNOUNCE_IPV4_ACTION_ID, tid) => map!(call!(AnnounceResponse::from_bytes_v4), |ann_res| {
TrackerResponse::new(tid, ResponseType::Announce(ann_res))
}) |
(::SCRAPE_ACTION_ID, tid) => map!(call!(ScrapeResponse::from_bytes), |scr_res| {
TrackerResponse::new(tid, ResponseType::Scrape(scr_res))
}) |
(ERROR_ACTION_ID, tid) => map!(call!(ErrorResponse::from_bytes), |err_res| {
TrackerResponse::new(tid, ResponseType::Error(err_res))
}) |
(::ANNOUNCE_IPV6_ACTION_ID, tid) => map!(call!(AnnounceResponse::from_bytes_v6), |ann_req| {
TrackerResponse::new(tid, ResponseType::Announce(ann_req))
})
)
}