wikifeet 1.0.0

WikiFeet (The collaborative celebrity feet website) crawler
Documentation
/*
 * Copyright 2021 XXIV
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
use std::io::Read;
use json::{JsonValue, parse};
use regex::Regex;
use crate::error::WikiFeetError;

#[doc(hidden)]
const PATTERN_ROMAN_FEET: &str = r#"\["Country", "Favor longer second toe"\],(.*?\s*\s])"#;
#[doc(hidden)]
const PATTERN_FOOT_TATTOOS: &str = r#"\["Country", "Favor foot tattoos"\],(.*?\s*\s])"#;
#[doc(hidden)]
const PATTERN_PAINTED_TOES: &str = r#"\["Country", "Favor painted toes"\],(.*?\s*\s])"#;
#[doc(hidden)]
const PATTERN_SECRET_FEET_LOVER: &str = r#"\["Country", "Keep it secret"\],(.*?\s*\s])"#;

/// Gets WikiFeet stats by country.
pub struct WikiFeetCountryStats {
    country_name: String
}

#[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 country(&info) {
                Some(data) => {
                    match parse(&data) {
                        Ok(json) => {
                            if json[key].is_null() {
                                None
                            } else {
                                Some(json[key].to_string())
                            }
                        },
                        Err(_) => None
                    }
                },
                None => None
            }
        },
        None => None
    }
}

#[doc(hidden)]
fn country(info: &str) -> Option<String> {
    match parse(info) {
        Ok(data) => {
            let ref mut json = JsonValue::new_object();
            let mut i = 0;
            while i < data["country"].len() {
                json[data["country"][i][0].to_string().to_uppercase()] = data["country"][i][1]["f"].to_string().into();
                i +=1
            }
            Some(json.to_string())
        },
        Err(_) => None
    }
}

#[doc(hidden)]
fn roman_feet_info() -> Option<String> {
    match http("https://www.wikifeet.com/polls/1") {
        Some(data) => {
            match Regex::new(PATTERN_ROMAN_FEET) {
                Ok(regex) => {
                    match regex.captures(&data) {
                        Some(cap) => {
                            let capture = match cap.get(1) {
                                Some(data) => data.as_str(),
                                None => return None
                            };
                            Some(format!("{{\"country\":[{}}}", capture))
                        },
                        None => None
                    }
                },
                Err(_) => None
            }
        },
        None => None
    }
}

#[doc(hidden)]
fn foot_tattoos_info() -> Option<String> {
    match http("https://www.wikifeet.com/polls/2") {
        Some(data) => {
            match Regex::new(PATTERN_FOOT_TATTOOS) {
                Ok(regex) => {
                    match regex.captures(&data) {
                        Some(cap) => {
                            let capture = match cap.get(1) {
                                Some(data) => data.as_str(),
                                None => return None
                            };
                            Some(format!("{{\"country\":[{}}}", capture))
                        },
                        None => None
                    }
                },
                Err(_) => None
            }
        },
        None => None
    }
}

#[doc(hidden)]
fn painted_toes_info() -> Option<String> {
    match http("https://www.wikifeet.com/polls/3") {
        Some(data) => {
            match Regex::new(PATTERN_PAINTED_TOES) {
                Ok(regex) => {
                    match regex.captures(&data) {
                        Some(cap) => {
                            let capture = match cap.get(1) {
                                Some(data) => data.as_str(),
                                None => return None
                            };
                            Some(format!("{{\"country\":[{}}}", capture))
                        },
                        None => None
                    }
                },
                Err(_) => None
            }
        },
        None => None
    }
}

#[doc(hidden)]
fn secret_feet_lover_info() -> Option<String> {
    match http("https://www.wikifeet.com/polls/4") {
        Some(data) => {
            match Regex::new(PATTERN_SECRET_FEET_LOVER) {
                Ok(regex) => {
                    match regex.captures(&data) {
                        Some(cap) => {
                            let capture = match cap.get(1) {
                                Some(data) => data.as_str(),
                                None => return None
                            };
                            Some(format!("{{\"country\":[{}}}", capture))
                        },
                        None => None
                    }
                },
                Err(_) => None
            }
        },
        None => None
    }
}

impl WikiFeetCountryStats {

    /// `country_name` The name of the country.
    pub fn new(country_name: &str) -> Self {
        Self {
            country_name: country_name.to_uppercase()
        }
    }

    /// Return the stats of the people who like roman feet.
    pub fn roman_feet(&self) -> Result<String, WikiFeetError> {
        match json(roman_feet_info(),self.country_name.as_str()) {
            Some(data) => {
                Ok(data)
            },
            None => Err(WikiFeetError::Null(String::from("null")))
        }
    }

    /// Return the stats of the people who like foot tattoos.
    pub fn foot_tattoos(&self) -> Result<String, WikiFeetError> {
        match json(foot_tattoos_info(),self.country_name.as_str()) {
            Some(data) => {
                Ok(data)
            },
            None => Err(WikiFeetError::Null(String::from("null")))
        }
    }

    /// Return the stats of the people who like painted toes.
    pub fn painted_toes(&self) -> Result<String, WikiFeetError> {
        match json(painted_toes_info(),self.country_name.as_str()) {
            Some(data) => {
                Ok(data)
            },
            None => Err(WikiFeetError::Null(String::from("null")))
        }
    }

    /// Return the stats of the people who like feet and keep it secret.
    pub fn secret_feet_lover(&self) -> Result<String, WikiFeetError> {
        match json(secret_feet_lover_info(),self.country_name.as_str()) {
            Some(data) => {
                Ok(data)
            },
            None => Err(WikiFeetError::Null(String::from("null")))
        }
    }
}