proxie 0.1.1

Library for making requests through HTTP or SOCKS proxy
Documentation
use std::{
    string::ToString,
    net::{Ipv4Addr, Ipv6Addr, SocketAddr},
};
use crate::error::*;

pub(crate) enum TargetHost {
    IPv4(Ipv4Addr),
    IPv6(Ipv6Addr),
    Hostname(String),
}

pub struct Target {
    pub(crate) host: TargetHost,
    pub(crate) port: u16,
}

impl Target {
    pub(crate) fn new(host: TargetHost, port: u16) -> Self {
        Self {
            host,
            port,
        }
    }
}

impl ToString for Target {
    fn to_string(&self) -> String {
        match &self.host {
            TargetHost::IPv4(host) => format!("{}:{}", host, self.port),
            TargetHost::IPv6(host) => format!("{}:{}", host, self.port),
            TargetHost::Hostname(host) => format!("{}:{}", host, self.port),
        }
    }
}

pub trait ToTarget {
    fn to_target(&self) -> Result<Target, MalformedTargetError>;
}

impl ToTarget for &str {
    fn to_target(&self) -> Result<Target, MalformedTargetError> {
        let colon_pos = match self.find(':') {
            Some(pos) => pos,
            None => return Err(MalformedTargetError),
        };

        let host = &self[0..colon_pos];
        let port_str = &self[colon_pos + 1..];

        let port = match port_str.parse::<u16>() {
            Ok(port) => port,
            Err(_) => return Err(MalformedTargetError),
        };

        let host = if host.starts_with('[') && host.ends_with(']') {
            match host[1..host.len() - 1].parse::<Ipv6Addr>() {
                Ok(ip) => TargetHost::IPv6(ip),
                Err(_) => return Err(MalformedTargetError),
            }
        } else {
            match host.parse::<Ipv4Addr>() {
                Ok(ip) => TargetHost::IPv4(ip),
                Err(_) => {
                    if host.is_empty() || host.ends_with('.') {
                        return Err(MalformedTargetError);
                    }

                    if host.chars().any(|c| !c.is_ascii_alphanumeric() && c != '.' && c != '-') {
                        return Err(MalformedTargetError);
                    }

                    TargetHost::Hostname(host.into())
                },
            }
        };

        Ok(Target::new(host, port))
    }
}

impl ToTarget for String {
    fn to_target(&self) -> Result<Target, MalformedTargetError> {
        (&**self).to_target()
    }
}

impl ToTarget for &String {
    fn to_target(&self) -> Result<Target, MalformedTargetError> {
        (&**self).to_target()
    }
}

impl ToTarget for SocketAddr {
    fn to_target(&self) -> Result<Target, MalformedTargetError> {
        let host = match self {
            SocketAddr::V4(socket) => TargetHost::IPv4(*socket.ip()),
            SocketAddr::V6(socket) => TargetHost::IPv6(*socket.ip()),
        };
        let port = self.port();

        Ok(Target::new(host, port))
    }
}