use crate::filters::Filter;
use crate::sorters::Sorter;
use reqwest;
use serde_derive::Deserialize;
use serde_json;
use std::collections::HashSet;
use std::hash::{Hash, Hasher};
use std::iter::FromIterator;
#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
pub enum ServerCategory {
Standard,
P2P,
Obfuscated,
Dedicated,
Tor,
Double,
UnknownServer,
}
impl From<String> for ServerCategory {
fn from(input: String) -> ServerCategory {
match input.as_ref() {
"Standard VPN servers" => ServerCategory::Standard,
"P2P" => ServerCategory::P2P,
"Double VPN" => ServerCategory::Double,
"Onion Over VPN" => ServerCategory::Tor,
"Obfuscated Servers" => ServerCategory::Obfuscated,
"Dedicated IP" => ServerCategory::Dedicated,
_ => ServerCategory::UnknownServer,
}
}
}
#[derive(Debug, Deserialize, PartialEq, Clone)]
struct ApiCategory {
pub name: String,
}
#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
pub struct Features {
pub ikev2: bool,
pub openvpn_udp: bool,
pub openvpn_tcp: bool,
pub socks: bool,
pub proxy: bool,
pub pptp: bool,
pub l2tp: bool,
pub openvpn_xor_udp: bool,
pub openvpn_xor_tcp: bool,
pub proxy_cybersec: bool,
pub proxy_ssl: bool,
pub proxy_ssl_cybersec: bool,
pub wireguard_udp: bool,
}
#[derive(Debug, Deserialize)]
struct ApiServer {
pub flag: String,
pub domain: String,
pub load: u8,
pub categories: Vec<ApiCategory>,
pub features: Features,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Server {
pub flag: String,
pub domain: String,
pub load: u8,
pub categories: Vec<ServerCategory>,
pub features: Features,
}
impl Hash for Server {
fn hash<H: Hasher>(&self, hasher: &mut H) {
self.domain.hash(hasher);
}
}
impl From<ApiServer> for Server {
fn from(api_server: ApiServer) -> Server {
Server {
flag: api_server.flag,
domain: api_server.domain,
load: api_server.load,
categories: Vec::from_iter(
api_server
.categories
.into_iter()
.map(|server_type| ServerCategory::from(server_type.name)),
),
features: api_server.features,
}
}
}
impl Server {
pub fn name(&self) -> Option<&str> {
use regex::Regex;
let re = Regex::new(r"(.+)\.nordvpn.com").unwrap();
let caps = match re.captures(&self.domain) {
Some(caps) => caps,
None => {
return None;
}
};
match caps.get(1) {
Some(matches) => Some(matches.as_str()),
None => None,
}
}
}
pub struct Servers {
pub servers: Vec<Server>,
}
impl Servers {
fn from_txt(txt: &str) -> Result<Servers, Box<dyn std::error::Error>> {
let api_servers: Vec<ApiServer> = serde_json::from_str(&txt)?;
Ok(Servers {
servers: Vec::from_iter(api_servers.into_iter().map(Server::from)),
})
}
pub fn from_api() -> Result<Servers, Box<dyn std::error::Error>> {
let data = reqwest::blocking::get("https://nordvpn.com/api/server")?;
let text = data.text()?;
Self::from_txt(&text)
}
pub fn dummy_data() -> Servers {
let text = std::fs::read_to_string("dummydata").unwrap();
Self::from_txt(&text).unwrap()
}
pub fn flags(&self) -> HashSet<&str> {
HashSet::from_iter(self.servers.iter().map(|server| server.flag.as_ref()))
}
pub fn perfect_server(&self) -> Option<Server> {
match self.servers.get(0) {
Some(x) => Some(x.clone()),
None => None,
}
}
}
#[derive(PartialEq)]
pub enum Protocol {
Udp,
Tcp,
Pptp,
L2tp,
OpenVPNXTcp,
OpenVPNXUdp,
Socks,
CyberSecProxy,
SslProxy,
CyberSecSslProxy,
Proxy,
WireGuardUdp,
}
impl Servers {
pub fn filter(&mut self, filter: &dyn Filter) {
(&mut self.servers).retain(|server| filter.filter(&server))
}
pub fn sort(&mut self, sorter: &dyn Sorter) {
(&mut self.servers).sort_unstable_by(|x, y| sorter.sort(x, y));
}
pub fn cut(&mut self, max: usize) {
self.servers.truncate(max);
}
}