use regex::{Regex};
use std::{rand};
use std::num::{Int};
use std::io::{IoError, IoResult, IoErrorKind, InvalidInput, ConnectionFailed};
use std::io::net::addrinfo;
use std::io::net::udp::{UdpSocket};
use std::io::net::ip::{SocketAddr, Ipv4Addr, IpAddr, Ipv6Addr};
pub const PEER_ID_LEN: uint = 20;
pub type SPeerID = [u8; PEER_ID_LEN];
pub type UPeerID = [u8];
pub const INFO_HASH_LEN: uint = 20;
pub type SInfoHash = [u8; INFO_HASH_LEN];
pub type UInfoHash = [u8];
pub const BTP_10_LEN: uint = 19;
pub type SBTP10 = [u8; BTP_10_LEN];
pub type UBTP10 = [u8];
pub enum Choice<O, T> {
One(O),
Two(T)
}
#[derive(Copy)]
pub enum Transport {
TCP,
UDP,
HTTP
}
const PEER_ID_PREFIX: &'static str = "RBT-0.1.1--";
static URL_REGEX: Regex = regex!(r"\A(\w+)://([^ ]+?)(?::(\d+))?(/.*)");
pub fn get_net_addrs() -> IoResult<Vec<IpAddr>> {
let addr_list = try!(addrinfo::get_host_addresses(""));
let addr_list = addr_list.into_iter().filter(|&addr|
match addr {
Ipv4Addr(..) => true,
Ipv6Addr(..) => false
}
).collect();
Ok(addr_list)
}
pub fn get_udp_sock(mut addr: SocketAddr, mut attempts: uint) -> IoResult<UdpSocket> {
let mut udp_socket = UdpSocket::bind(addr);
attempts -= 1;
while udp_socket.is_err() && attempts > 0 {
addr.port += 1;
attempts -= 1;
udp_socket = UdpSocket::bind(addr);
}
match udp_socket {
Ok(n) => Ok(n),
_ => Err(get_error(ConnectionFailed, "Could Not Bind To A UDP Port"))
}
}
pub fn get_udp_wait(attempt: uint) -> u64 {
(15 * 2i.pow(attempt)) as u64
}
pub fn gen_peer_id() -> [u8; 20] {
let mut bytes = [0u8; 20];
for (byte, pref) in bytes.iter_mut().zip(PEER_ID_PREFIX.chars()) {
*byte = pref as u8;
}
for i in range(PEER_ID_PREFIX.len(), 20) {
bytes[i] = rand::random::<char>() as u8;
}
bytes
}
pub fn get_transport(url: &str) -> IoResult<Transport> {
let trans_str = try!(try!(URL_REGEX.captures(url).ok_or(
get_error(InvalidInput, "Transport Protocol Not Found In url")
)).at(1).ok_or(
get_error(InvalidInput, "Transport Protocol Not Found In url")
));
if trans_str.len() == 0 {
return Err(get_error(InvalidInput, "Transport Protocol Not Found In url"));
}
match trans_str {
"http" => Ok(Transport::HTTP),
"tcp" => Ok(Transport::TCP),
"udp" => Ok(Transport::UDP),
_ => Err(get_error(InvalidInput, "Transport Protocol Not Found In url"))
}
}
pub fn get_sockaddr(url: &str) -> IoResult<SocketAddr> {
let captures = try!(URL_REGEX.captures(url).ok_or(
get_error(InvalidInput, "Hostname And/Or Port Number Not Found In url")
));
let (host_str, port_str) = (captures.at(2).unwrap_or(""), captures.at(3).unwrap_or(""));
if host_str.len() == 0 || port_str.len() == 0 {
return Err(get_error(InvalidInput, "Hostname And/Or Port Number Not Found In url"))
}
let host_ip = try!(addrinfo::get_host_addresses(host_str))[0];
let port_num = try!(port_str.parse::<u16>().ok_or(
get_error(InvalidInput, "Invalid Port Number Found In url")
));
Ok(SocketAddr{ ip: host_ip, port: port_num })
}
pub fn get_path(url: &str) -> IoResult<&str> {
let path_str = match URL_REGEX.captures(url) {
Some(n) => n.at(4).unwrap_or(""),
None => return Err(get_error(InvalidInput, "No Path Found In url"))
};
Ok(path_str)
}
pub fn get_error(err_type: IoErrorKind, msg: &'static str) -> IoError {
IoError{ kind: err_type, desc: msg, detail: None }
}
#[cfg(test)]
mod tests {
use super::{get_udp_wait, get_path};
#[test]
fn positive_get_path() {
assert_eq!(get_path("http://test.com:80/test_path").unwrap(), "/test_path");
assert_eq!(get_path("http://test.com/test_path").unwrap(), "/test_path");
}
#[test]
fn positive_get_udp_wait() {
assert_eq!(get_udp_wait(0), 15u64);
assert_eq!(get_udp_wait(1), 30u64);
assert_eq!(get_udp_wait(-1), 0u64);
}
}