use crate::{utils, Category, Cookie, Country, Keywords, Lang, Period, Property};
use chrono::NaiveDate;
use reqwest::{blocking::ClientBuilder, header, Url};
use serde_json::Value;
use std::string::ToString;
use strum::EnumProperty;
#[derive(Clone, Debug)]
pub struct Client {
pub client: reqwest::blocking::Client,
pub cookie: Cookie,
pub country: Country,
pub keywords: Keywords,
pub lang: Lang,
pub property: Property,
pub time: String,
pub category: Category,
pub response: Value,
}
impl Default for Client {
fn default() -> Self {
Self {
client: reqwest::blocking::Client::default(),
cookie: Cookie::new(),
response: serde_json::from_str("{}").unwrap(),
keywords: Keywords::default(),
time: Period::OneYear.to_string(),
country: Country::ALL,
property: Property::Web,
lang: Lang::EN,
category: Category::All,
}
}
}
impl Client {
const EXPLORE_ENDPOINT: &'static str = "https://trends.google.com/trends/api/explore";
const BAD_CHARACTER: usize = 4;
pub fn new(keywords: Keywords, country: Country) -> Self {
let mut headers = header::HeaderMap::new();
headers = Cookie::new().add_to_header(headers);
let client = ClientBuilder::new().default_headers(headers).build();
let client = match client {
Ok(client) => client,
Err(error) => panic!(
"Problem constructing the client while retrieving access token: {:?}",
error
),
};
Self {
client,
country,
keywords,
..Client::default()
}
}
pub fn with_keywords(mut self, keywords: Keywords) -> Self {
self.keywords = keywords;
self
}
pub fn with_lang(mut self, lang: Lang) -> Self {
self.lang = lang;
self
}
pub fn with_category(mut self, category: Category) -> Self {
self.category = category;
self
}
pub fn with_property(mut self, property: Property) -> Self {
self.property = property;
self
}
pub fn with_period(mut self, period: impl ToString) -> Self {
self.time = period.to_string();
self
}
pub fn with_date(mut self, start_date: NaiveDate, end_date: NaiveDate) -> Self {
fn convert(date: NaiveDate) -> String {
date.format("%Y-%m-%d").to_string()
}
let custom_period = format!("{} {}", convert(start_date), convert(end_date));
self.time = custom_period;
self
}
#[allow(dead_code)]
pub fn with_filter(
mut self,
category: Category,
property: Property,
period: String,
lang: Lang,
) -> Self {
self.category = category;
self.property = property;
self.time = period;
self.lang = lang;
self
}
pub fn build(mut self) -> Self {
let url = Url::parse(Self::EXPLORE_ENDPOINT).unwrap();
let comparison_item = self.build_comparison_item();
let resp = self
.client
.get(url)
.query(&[
("hl", self.lang.to_string().as_str()),
("geo", self.country.to_string().as_str()),
("tz", "-120"),
("req", &comparison_item),
("tz", "-120"),
])
.send();
let resp = match resp {
Ok(resp) => resp,
Err(error) => panic!("Can't get client response: {:?}", error),
};
let body = resp.text().unwrap();
let clean_response = utils::sanitize_response(&body, Self::BAD_CHARACTER).to_string();
self.response = serde_json::from_str(clean_response.as_str()).unwrap();
self
}
fn build_comparison_item(&self) -> String {
let mut comparison_item = String::new();
let keys_it = self.keywords.keywords.iter();
for key in keys_it {
let index_value = format!(
"{{'keyword':'{}','geo':'{}','time':'{}'}},",
key, self.country, self.time
);
comparison_item.push_str(&index_value);
}
let id = self.category.get_int("Id").unwrap_or(0);
format!(
"{{'comparisonItem':[{}],'category':{},'property':'{}'}}",
comparison_item.as_str(),
id,
self.property
)
}
}