use std::io::Read;
use json::{JsonValue, object, parse};
use rand::seq::SliceRandom;
use regex::Regex;
use crate::error::WikiFeetError;
#[doc(hidden)]
const PATTERN_RANKS_LIST: &str = r#"<h2>#(.*?): (.*?)</h2><div class='round8 celebbox'>\s*<div class=boxcont><a href="/(.*?)">"#;
#[doc(hidden)]
const PATTERN_RATING_INFO: &str = r#"width:100%'>(.*?)<br><span style='color:#abc'>(.*?)</span>"#;
#[doc(hidden)]
const PATTERN_ID: &str = r#"messanger\['gdata'\] = (\[.*?\]);"#;
#[doc(hidden)]
const PATTERN_SHOE_SIZE: &str = r#"id=ssize_label>(.*?)<a"#;
#[doc(hidden)]
const PATTERN_BIRTH_PLACE: &str = r#"id=nation_label>(.*?)<a"#;
#[doc(hidden)]
const PATTERN_BIRTH_DATE: &str = r#"id=bdate_label>(.*?)<a"#;
#[doc(hidden)]
const PATTERN_RATING: &str = r#"white-space:nowrap' > \((.*?) feet\)</div>"#;
#[doc(hidden)]
const PATTERN_RATING_STATS: &str = r#"Feet rating stats \((.*?) (.*?)<br>"#;
#[doc(hidden)]
const PATTERN_RATING_GORGEOUS: &str = r#"Rating(.*?) \((.*?) feet\)</div>"#;
#[doc(hidden)]
const PATTERN_IMDB: &str = r#"www.imdb.com/(.*?)'(.*?)Go to IMDb page"#;
pub struct FeetOfTheYear {
#[doc(hidden)]
model_info: Option<String>
}
#[doc(hidden)]
struct FeetOfTheYearInternal {
model_id: i32,
index: usize
}
#[doc(hidden)]
fn http(url: &str) -> Option<String> {
match reqwest::blocking::get(url.to_string()) {
Ok(mut data) => {
let mut body = String::new();
match data.read_to_string(&mut body) {
Ok(_) => Some(body),
Err(_) => None
}
},
Err(_) => None
}
}
#[doc(hidden)]
fn json(info: Option<String>, key: &str) -> Option<String> {
match info {
Some(info) => {
match parse(&info) {
Ok(json) => {
if json[key].is_null() {
None
} else {
Some(json[key].to_string())
}
},
Err(_) => None
}
},
None => None
}
}
impl FeetOfTheYear {
pub fn new(model_id: i32, index: usize) -> Self {
let mut internal = FeetOfTheYearInternal::new(model_id, index);
let model_info = internal.model_info();
Self {
model_info
}
}
#[doc(hidden)]
fn rating_info(&self) -> Option<String> {
match json(self.model_info.clone(),"url") {
Some(data_url) => {
match http(&data_url) {
Some(url) => {
match Regex::new(PATTERN_RATING_INFO) {
Ok(regex) => {
let mut json = JsonValue::new_object();
for cap in regex.captures_iter(&url) {
json[&cap[2]] = cap[1].into();
}
Some(json.to_string())
},
Err(_) => None
}
},
None => None
}
},
None => None
}
}
#[doc(hidden)]
fn id(&self) -> Option<String> {
match json(self.model_info.clone(),"url") {
Some(data_url) => {
match http(&data_url) {
Some(url) => {
match Regex::new(PATTERN_ID) {
Ok(regex) => {
match regex.captures(&url) {
Some(cap) => {
let capture = match cap.get(1) {
Some(data) => data.as_str(),
None => return None
};
match parse(&capture) {
Ok(json) => {
let mut list = vec![];
let mut i = 0;
while i < json.len(){
list.push(json[i]["pid"].to_string());
i +=1;
}
let mut random = rand::thread_rng();
match list.choose(&mut random) {
Some(id) => Some(id.to_string()),
None => None
}
},
Err(_) => None
}
},
None => None
}
},
Err(_) => None
}
},
None => None
}
},
None => None
}
}
pub fn model_name(&self) -> Result<String, WikiFeetError> {
match json(self.model_info.clone(),"name") {
Some(name) => Ok(name),
None => Err(WikiFeetError::Null(String::from("null")))
}
}
pub fn model_page(&self) -> Result<String, WikiFeetError> {
match json(self.model_info.clone(),"url") {
Some(data) => Ok(data),
None => Err(WikiFeetError::Null(String::from("null")))
}
}
pub fn thumbnail(&self) -> Result<String, WikiFeetError> {
match self.id() {
Some(id) => Ok(format!("https://thumbs.wikifeet.com/{}.jpg",id)),
None => Err(WikiFeetError::Null(String::from("null")))
}
}
pub fn image(&self) -> Result<String, WikiFeetError> {
match json(self.model_info.clone(),"username") {
Some(username) => {
match self.id() {
Some(id) => Ok(format!("https://pics.wikifeet.com/{}-feet-{}.jpg",username,id)),
None => Err(WikiFeetError::Null(String::from("null")))
}
},
None => Err(WikiFeetError::Null(String::from("null")))
}
}
pub fn shoe_size(&self) -> Result<String, WikiFeetError> {
match json(self.model_info.clone(),"url") {
Some(data_url) => {
match http(&data_url) {
Some(url) => {
match Regex::new(PATTERN_SHOE_SIZE) {
Ok(regex) => {
match regex.captures(&url) {
Some(cap) => {
match cap.get(1) {
Some(data) => Ok(data.as_str().to_string()),
None => Err(WikiFeetError::Null(String::from("null")))
}
},
None => Err(WikiFeetError::Null(String::from("null")))
}
},
Err(_) => Err(WikiFeetError::Null(String::from("null")))
}
},
None => Err(WikiFeetError::Null(String::from("null")))
}
},
None => Err(WikiFeetError::Null(String::from("null")))
}
}
pub fn birth_place(&self) -> Result<String, WikiFeetError> {
match json(self.model_info.clone(),"url") {
Some(data_url) => {
match http(&data_url) {
Some(url) => {
match Regex::new(PATTERN_BIRTH_PLACE) {
Ok(regex) => {
match regex.captures(&url) {
Some(cap) => {
match cap.get(1) {
Some(data) => Ok(data.as_str().to_string()),
None => Err(WikiFeetError::Null(String::from("null")))
}
},
None => Err(WikiFeetError::Null(String::from("null")))
}
},
Err(_) => Err(WikiFeetError::Null(String::from("null")))
}
},
None => Err(WikiFeetError::Null(String::from("null")))
}
},
None => Err(WikiFeetError::Null(String::from("null")))
}
}
pub fn birth_date(&self) -> Result<String, WikiFeetError> {
match json(self.model_info.clone(),"url") {
Some(data_url) => {
match http(&data_url) {
Some(url) => {
match Regex::new(PATTERN_BIRTH_DATE) {
Ok(regex) => {
match regex.captures(&url) {
Some(cap) => {
match cap.get(1) {
Some(data) => Ok(data.as_str().to_string()),
None => Err(WikiFeetError::Null(String::from("null")))
}
},
None => Err(WikiFeetError::Null(String::from("null")))
}
},
Err(_) => Err(WikiFeetError::Null(String::from("null")))
}
},
None => Err(WikiFeetError::Null(String::from("null")))
}
},
None => Err(WikiFeetError::Null(String::from("null")))
}
}
pub fn rating(&self) -> Result<String, WikiFeetError> {
match json(self.model_info.clone(),"url") {
Some(data_url) => {
match http(&data_url) {
Some(url) => {
match Regex::new(PATTERN_RATING) {
Ok(regex) => {
match regex.captures(&url) {
Some(cap) => {
match cap.get(1) {
Some(data) => Ok(data.as_str().to_string()),
None => Err(WikiFeetError::Null(String::from("null")))
}
},
None => {
match Regex::new(PATTERN_RATING_GORGEOUS) {
Ok(regex) => {
match regex.captures(&url) {
Some(cap) => {
match cap.get(2) {
Some(data) => Ok(data.as_str().to_string()),
None => Err(WikiFeetError::Null(String::from("null")))
}
},
None => Err(WikiFeetError::Null(String::from("null")))
}
},
Err(_) => Err(WikiFeetError::Null(String::from("null")))
}
}
}
},
Err(_) => Err(WikiFeetError::Null(String::from("null")))
}
},
None => Err(WikiFeetError::Null(String::from("null")))
}
},
None => Err(WikiFeetError::Null(String::from("null")))
}
}
pub fn beautiful_rating(&self) -> Result<String, WikiFeetError> {
match json(self.rating_info(),"beautiful") {
Some(data) => Ok(data),
None => Err(WikiFeetError::Null(String::from("null")))
}
}
pub fn nice_rating(&self) -> Result<String, WikiFeetError> {
match json(self.rating_info(),"nice") {
Some(data) => Ok(data),
None => Err(WikiFeetError::Null(String::from("null")))
}
}
pub fn ok_rating(&self) -> Result<String, WikiFeetError> {
match json(self.rating_info(),"ok") {
Some(data) => Ok(data),
None => Err(WikiFeetError::Null(String::from("null")))
}
}
pub fn bad_rating(&self) -> Result<String, WikiFeetError> {
match json(self.rating_info(),"bad") {
Some(data) => Ok(data),
None => Err(WikiFeetError::Null(String::from("null")))
}
}
pub fn ugly_rating(&self) -> Result<String, WikiFeetError> {
match json(self.rating_info(),"ugly") {
Some(data) => Ok(data),
None => Err(WikiFeetError::Null(String::from("null")))
}
}
pub fn rating_stats(&self) -> Result<String, WikiFeetError> {
match json(self.model_info.clone(),"url") {
Some(data_url) => {
match http(&data_url) {
Some(url) => {
match Regex::new(PATTERN_RATING_STATS) {
Ok(regex) => {
match regex.captures(&url) {
Some(cap) => {
match cap.get(1) {
Some(data) => Ok(data.as_str().to_string()),
None => Err(WikiFeetError::Null(String::from("null")))
}
},
None => Err(WikiFeetError::Null(String::from("null")))
}
},
Err(_) => Err(WikiFeetError::Null(String::from("null")))
}
},
None => Err(WikiFeetError::Null(String::from("null")))
}
},
None => Err(WikiFeetError::Null(String::from("null")))
}
}
pub fn imdb_page(&self) -> Result<String, WikiFeetError> {
match json(self.model_info.clone(),"url") {
Some(data_url) => {
match http(&data_url) {
Some(url) => {
match Regex::new(PATTERN_IMDB) {
Ok(regex) => {
match regex.captures(&url) {
Some(cap) => {
match cap.get(1) {
Some(data) => Ok(format!("https://www.imdb.com/{}",data.as_str())),
None => Err(WikiFeetError::Null(String::from("null")))
}
},
None => Err(WikiFeetError::Null(String::from("null")))
}
},
Err(_) => Err(WikiFeetError::Null(String::from("null")))
}
},
None => Err(WikiFeetError::Null(String::from("null")))
}
},
None => Err(WikiFeetError::Null(String::from("null")))
}
}
}
impl FeetOfTheYearInternal {
fn new(model_id: i32, index: usize) -> Self {
Self {
model_id,
index,
}
}
fn rank_list(&self) -> Option<Vec<JsonValue>> {
match http("https://www.wikifeet.com/feetoftheyear") {
Some(url) => {
match Regex::new(PATTERN_RANKS_LIST) {
Ok(regex) => {
let mut list: Vec<JsonValue> = vec![];
for cap in regex.captures_iter(&url) {
let model_id = match cap.get(1) {
Some(data) => data.as_str(),
None => return None
};
let model_name = match cap.get(2) {
Some(data) => data.as_str(),
None => return None
};
let model_username = match cap.get(3) {
Some(data) => data.as_str().replace(r#"_"#, "-"),
None => return None
};
let model_url = match cap.get(3) {
Some(data) => format!("https://www.wikifeet.com/{}", data.as_str()),
None => return None
};
list.push(object! {
id: model_id,
name: model_name,
username: model_username,
url: model_url
});
}
Some(list)
},
Err(_) => None
}
},
None => None
}
}
fn rank_info(&self) -> Option<Vec<JsonValue>> {
match self.rank_list() {
Some(data) => {
let mut list: Vec<JsonValue> = vec![];
for i in data.iter() {
if i["id"].to_string() == self.model_id.to_string() {
list.push(object! {
id: i["id"].to_string(),
name: i["name"].to_string(),
username: i["username"].to_string(),
url: i["url"].to_string()
})
}
}
Some(list)
}
None => None
}
}
fn model_info(&mut self) -> Option<String> {
match self.rank_info() {
Some(data) => {
if self.index == 1 {
self.index = 0
} else if self.index == 2 {
self.index = 1
}
match data.get(self.index) {
Some(json) => {
Some(object! {
id: json["id"].to_string(),
name: json["name"].to_string(),
username: json["username"].to_string(),
url: json["url"].to_string()
}.to_string())
},
None => {
match data.get(0) {
Some(json) => {
Some(object! {
id: json["id"].to_string(),
name: json["name"].to_string(),
username: json["username"].to_string(),
url: json["url"].to_string()
}.to_string())
},
None => None
}
}
}
},
None => None
}
}
}