use std::fmt::{Display, Formatter};
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
pub const URL: &str = "https://customsearch.googleapis.com/customsearch/v1";
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ErrorDetail {
pub code: u16,
pub message: String,
pub status: String,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ErrorBody {
pub error: ErrorDetail,
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub enum C2COff {
Off,
On,
}
impl Display for C2COff {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
C2COff::Off => write!(f, "0"),
C2COff::On => write!(f, "1"),
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum Cr {
Country(String),
Not(Box<Cr>),
And(Box<Cr>, Box<Cr>),
Or(Box<Cr>, Box<Cr>),
}
impl Display for Cr {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Cr::Country(country) => write!(f, "{}", country),
Cr::Not(cr) => write!(f, "-{}", cr),
Cr::And(cr1, cr2) => write!(f, "({}).({})", cr1, cr2),
Cr::Or(cr1, cr2) => write!(f, "({}|{})", cr1, cr2),
}
}
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub enum DateRestrict {
Day(u32),
Week(u32),
Month(u32),
Year(u32),
}
impl Display for DateRestrict {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
DateRestrict::Day(day) => write!(f, "d{}", day),
DateRestrict::Week(week) => write!(f, "w{}", week),
DateRestrict::Month(month) => write!(f, "m{}", month),
DateRestrict::Year(year) => write!(f, "y{}", year),
}
}
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub enum DuplicateContentFilter {
Off,
On,
}
impl Display for DuplicateContentFilter {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
DuplicateContentFilter::Off => write!(f, "0"),
DuplicateContentFilter::On => write!(f, "1"),
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum Gl {
CountryCode(String),
}
impl Display for Gl {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Gl::CountryCode(country_code) => write!(f, "{}", country_code),
}
}
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub enum ImgColorType {
Color,
Gray,
Mono,
Trans,
}
impl Display for ImgColorType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ImgColorType::Color => {
write!(f, "color")
}
ImgColorType::Gray => {
write!(f, "gray")
}
ImgColorType::Mono => {
write!(f, "mono")
}
ImgColorType::Trans => {
write!(f, "trans")
}
}
}
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub enum ImgDominantColor {
Black,
Blue,
Brown,
Gray,
Green,
Orange,
Pink,
Purple,
Red,
Teal,
White,
Yellow,
}
impl Display for ImgDominantColor {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ImgDominantColor::Black => {
write!(f, "black")
}
ImgDominantColor::Blue => {
write!(f, "blue")
}
ImgDominantColor::Brown => {
write!(f, "brown")
}
ImgDominantColor::Gray => {
write!(f, "gray")
}
ImgDominantColor::Green => {
write!(f, "green")
}
ImgDominantColor::Orange => {
write!(f, "orange")
}
ImgDominantColor::Pink => {
write!(f, "pink")
}
ImgDominantColor::Purple => {
write!(f, "purple")
}
ImgDominantColor::Red => {
write!(f, "red")
}
ImgDominantColor::Teal => {
write!(f, "teal")
}
ImgDominantColor::White => {
write!(f, "white")
}
ImgDominantColor::Yellow => {
write!(f, "yellow")
}
}
}
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub enum ImgSize {
Icon,
Small,
Medium,
Large,
Xlarge,
Xxlarge,
Huge,
}
impl Display for ImgSize {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ImgSize::Icon => {
write!(f, "icon")
}
ImgSize::Small => {
write!(f, "small")
}
ImgSize::Medium => {
write!(f, "medium")
}
ImgSize::Large => {
write!(f, "large")
}
ImgSize::Xlarge => {
write!(f, "xlarge")
}
ImgSize::Xxlarge => {
write!(f, "xxlarge")
}
ImgSize::Huge => {
write!(f, "huge")
}
}
}
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub enum ImgType {
Face,
Photo,
Clipart,
Lineart,
Animated,
Stock,
}
impl Display for ImgType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ImgType::Face => {
write!(f, "face")
}
ImgType::Photo => {
write!(f, "photo")
}
ImgType::Clipart => {
write!(f, "clipart")
}
ImgType::Lineart => {
write!(f, "lineart")
}
ImgType::Animated => {
write!(f, "animated")
}
ImgType::Stock => {
write!(f, "stock")
}
}
}
}
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub enum Lr {
LangAr,
LangBg,
LangCa,
LangCs,
LangDa,
LangDe,
LangEl,
LangEn,
LangEs,
LangEt,
LangFi,
LangFr,
LangHr,
LangHu,
LangId,
LangIs,
LangIt,
LangIw,
LangJa,
LangKo,
LangLt,
LangLv,
LangNl,
LangNo,
LangPl,
LangPt,
LangRo,
LangRu,
LangSk,
LangSl,
LangSr,
LangSv,
LangTr,
LangZhCN,
LangZhTW,
}
impl Display for Lr {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Lr::LangAr => write!(f, "lang_ar"),
Lr::LangBg => write!(f, "lang_bg"),
Lr::LangCa => write!(f, "lang_ca"),
Lr::LangCs => write!(f, "lang_cs"),
Lr::LangDa => write!(f, "lang_da"),
Lr::LangDe => write!(f, "lang_de"),
Lr::LangEl => write!(f, "lang_el"),
Lr::LangEn => write!(f, "lang_en"),
Lr::LangEs => write!(f, "lang_es"),
Lr::LangEt => write!(f, "lang_et"),
Lr::LangFi => write!(f, "lang_fi"),
Lr::LangFr => write!(f, "lang_fr"),
Lr::LangHr => write!(f, "lang_hr"),
Lr::LangHu => write!(f, "lang_hu"),
Lr::LangId => write!(f, "lang_id"),
Lr::LangIs => write!(f, "lang_is"),
Lr::LangIt => write!(f, "lang_it"),
Lr::LangIw => write!(f, "lang_iw"),
Lr::LangJa => write!(f, "lang_ja"),
Lr::LangKo => write!(f, "lang_ko"),
Lr::LangLt => write!(f, "lang_lt"),
Lr::LangLv => write!(f, "lang_lv"),
Lr::LangNl => write!(f, "lang_nl"),
Lr::LangNo => write!(f, "lang_no"),
Lr::LangPl => write!(f, "lang_pl"),
Lr::LangPt => write!(f, "lang_pt"),
Lr::LangRo => write!(f, "lang_ro"),
Lr::LangRu => write!(f, "lang_ru"),
Lr::LangSk => write!(f, "lang_sk"),
Lr::LangSl => write!(f, "lang_sl"),
Lr::LangSr => write!(f, "lang_sr"),
Lr::LangSv => write!(f, "lang_sv"),
Lr::LangTr => write!(f, "lang_tr"),
Lr::LangZhCN => write!(f, "lang_zh-CN"),
Lr::LangZhTW => write!(f, "lang_zh-TW"),
}
}
}
impl TryFrom<&str> for Lr {
type Error = &'static str;
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
"lang_ar" => Ok(Lr::LangAr),
"lang_bg" => Ok(Lr::LangBg),
"lang_ca" => Ok(Lr::LangCa),
"lang_cs" => Ok(Lr::LangCs),
"lang_da" => Ok(Lr::LangDa),
"lang_de" => Ok(Lr::LangDe),
"lang_el" => Ok(Lr::LangEl),
"lang_en" => Ok(Lr::LangEn),
"lang_es" => Ok(Lr::LangEs),
"lang_et" => Ok(Lr::LangEt),
"lang_fi" => Ok(Lr::LangFi),
"lang_fr" => Ok(Lr::LangFr),
"lang_hr" => Ok(Lr::LangHr),
"lang_hu" => Ok(Lr::LangHu),
"lang_id" => Ok(Lr::LangId),
"lang_is" => Ok(Lr::LangIs),
"lang_it" => Ok(Lr::LangIt),
"lang_iw" => Ok(Lr::LangIw),
"lang_ja" => Ok(Lr::LangJa),
"lang_ko" => Ok(Lr::LangKo),
"lang_lt" => Ok(Lr::LangLt),
"lang_lv" => Ok(Lr::LangLv),
"lang_nl" => Ok(Lr::LangNl),
"lang_no" => Ok(Lr::LangNo),
"lang_pl" => Ok(Lr::LangPl),
"lang_pt" => Ok(Lr::LangPt),
"lang_ro" => Ok(Lr::LangRo),
"lang_ru" => Ok(Lr::LangRu),
"lang_sk" => Ok(Lr::LangSk),
"lang_sl" => Ok(Lr::LangSl),
"lang_sr" => Ok(Lr::LangSr),
"lang_sv" => Ok(Lr::LangSv),
"lang_tr" => Ok(Lr::LangTr),
"lang_zh-CN" => Ok(Lr::LangZhCN),
"lang_zh-TW" => Ok(Lr::LangZhTW),
_ => Err("Invalid value"),
}
}
}
#[allow(missing_docs)]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum SearchResultNumber {
One,
Two,
Three,
Four,
Five,
Six,
Seven,
Eight,
Nine,
Ten,
}
impl TryFrom<u32> for SearchResultNumber {
type Error = &'static str;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
1 => Ok(SearchResultNumber::One),
2 => Ok(SearchResultNumber::Two),
3 => Ok(SearchResultNumber::Three),
4 => Ok(SearchResultNumber::Four),
5 => Ok(SearchResultNumber::Five),
6 => Ok(SearchResultNumber::Six),
7 => Ok(SearchResultNumber::Seven),
8 => Ok(SearchResultNumber::Eight),
9 => Ok(SearchResultNumber::Nine),
10 => Ok(SearchResultNumber::Ten),
_ => Err("Invalid search result number"),
}
}
}
impl Display for SearchResultNumber {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
SearchResultNumber::One => write!(f, "1"),
SearchResultNumber::Two => write!(f, "2"),
SearchResultNumber::Three => write!(f, "3"),
SearchResultNumber::Four => write!(f, "4"),
SearchResultNumber::Five => write!(f, "5"),
SearchResultNumber::Six => write!(f, "6"),
SearchResultNumber::Seven => write!(f, "7"),
SearchResultNumber::Eight => write!(f, "8"),
SearchResultNumber::Nine => write!(f, "9"),
SearchResultNumber::Ten => write!(f, "10"),
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum Rights {
CcPublicdomain,
CcAttribute,
CcSharealike,
CcNoncommercial,
CcNonderived,
And(Box<Rights>, Box<Rights>),
}
impl Display for Rights {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Rights::CcPublicdomain => write!(f, "cc_publicdomain"),
Rights::CcAttribute => write!(f, "cc_attribute"),
Rights::CcSharealike => write!(f, "cc_sharealike"),
Rights::CcNoncommercial => write!(f, "cc_noncommercial"),
Rights::CcNonderived => write!(f, "cc_nonderived"),
Rights::And(left, right) => write!(f, "{}%7C{}", left, right),
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum Safe {
Active,
Off,
}
impl Display for Safe {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Safe::Active => write!(f, "active"),
Safe::Off => write!(f, "off"),
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum SearchType {
Image,
}
impl Display for SearchType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
SearchType::Image => write!(f, "image"),
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum SiteSearchFilter {
E,
I,
}
impl Display for SiteSearchFilter {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
SiteSearchFilter::E => write!(f, "e"),
SiteSearchFilter::I => write!(f, "i"),
}
}
}
#[derive(Default, Clone, Serialize, Deserialize)]
pub struct QueryParameters {
c2coff: Option<C2COff>,
cr: Option<Box<Cr>>,
cx: Option<String>,
key: Option<String>,
date_restrict: Option<String>,
exact_terms: Option<String>,
exclude_terms: Option<String>,
file_type: Option<String>,
filter: Option<DuplicateContentFilter>,
gl: Option<Gl>,
low_range: Option<String>,
high_range: Option<String>,
hl: Option<String>,
hq: Option<String>,
img_color_type: Option<ImgColorType>,
img_dominant_color: Option<ImgDominantColor>,
img_size: Option<ImgSize>,
img_type: Option<ImgType>,
link_site: Option<String>,
lr: Option<Lr>,
num: Option<SearchResultNumber>,
or_terms: Option<String>,
q: Option<String>,
related_site: Option<String>,
rights: Option<Box<Rights>>,
safe: Option<Safe>,
search_type: Option<SearchType>,
site_search: Option<String>,
site_search_filter: Option<SiteSearchFilter>,
sort: Option<String>,
start: Option<u32>,
}
impl QueryParameters {
pub fn build(&mut self) -> Self {
self.clone()
}
pub fn builder() -> QueryParameters {
QueryParameters {
c2coff: None,
cr: None,
cx: None,
key: None,
date_restrict: None,
exact_terms: None,
exclude_terms: None,
file_type: None,
filter: None,
gl: None,
low_range: None,
high_range: None,
hl: None,
hq: None,
img_color_type: None,
img_dominant_color: None,
img_size: None,
img_type: None,
link_site: None,
lr: None,
num: None,
or_terms: None,
q: None,
related_site: None,
rights: None,
safe: None,
search_type: None,
site_search: None,
site_search_filter: None,
sort: None,
start: None,
}
}
pub fn to_parameters(&self) -> Vec<(String, String)> {
let mut params = vec![];
if let Some(key) = &self.key {
params.push(("key".to_string(), key.to_string()));
}
if let Some(cx) = &self.cx {
params.push(("cx".to_string(), cx.to_string()));
}
if let Some(c2coff) = &self.c2coff {
params.push(("c2coff".to_string(), c2coff.to_string()));
}
if let Some(cr) = &self.cr {
params.push(("cr".to_string(), cr.to_string()));
}
if let Some(date_restrict) = &self.date_restrict {
params.push(("dateRestrict".to_string(), date_restrict.to_string()));
}
if let Some(exact_terms) = &self.exact_terms {
params.push(("exactTerms".to_string(), exact_terms.to_string()));
}
if let Some(exclude_terms) = &self.exclude_terms {
params.push(("excludeTerms".to_string(), exclude_terms.to_string()));
}
if let Some(file_type) = &self.file_type {
params.push(("fileType".to_string(), file_type.to_string()));
}
if let Some(filter) = &self.filter {
params.push(("filter".to_string(), filter.to_string()));
}
if let Some(gl) = &self.gl {
params.push(("gl".to_string(), gl.to_string()));
}
if let Some(low_range) = &self.low_range {
params.push(("lowRange".to_string(), low_range.to_string()));
}
if let Some(high_range) = &self.high_range {
params.push(("highRange".to_string(), high_range.to_string()));
}
if let Some(hl) = &self.hl {
params.push(("hl".to_string(), hl.to_string()));
}
if let Some(hq) = &self.hq {
params.push(("hq".to_string(), hq.to_string()));
}
if let Some(img_color_type) = &self.img_color_type {
params.push(("imgColorType".to_string(), img_color_type.to_string()));
}
if let Some(img_dominant_color) = &self.img_dominant_color {
params.push((
"imgDominantColor".to_string(),
img_dominant_color.to_string(),
));
}
if let Some(img_size) = &self.img_size {
params.push(("imgSize".to_string(), img_size.to_string()));
}
if let Some(img_type) = &self.img_type {
params.push(("imgType".to_string(), img_type.to_string()));
}
if let Some(link_site) = &self.link_site {
params.push(("linkSite".to_string(), link_site.to_string()));
}
if let Some(lr) = &self.lr {
params.push(("lr".to_string(), lr.to_string()));
}
if let Some(num) = &self.num {
params.push(("num".to_string(), num.to_string()));
}
if let Some(or_terms) = &self.or_terms {
params.push(("orTerms".to_string(), or_terms.to_string()));
}
if let Some(q) = &self.q {
params.push(("q".to_string(), q.to_string()));
}
if let Some(related_site) = &self.related_site {
params.push(("relatedSite".to_string(), related_site.to_string()));
}
if let Some(rights) = &self.rights {
params.push(("rights".to_string(), rights.to_string()));
}
if let Some(safe) = &self.safe {
params.push(("safe".to_string(), safe.to_string()));
}
if let Some(search_type) = &self.search_type {
params.push(("searchType".to_string(), search_type.to_string()));
}
if let Some(site_search) = &self.site_search {
params.push(("siteSearch".to_string(), site_search.to_string()));
}
if let Some(site_search_filter) = &self.site_search_filter {
params.push((
"siteSearchFilter".to_string(),
site_search_filter.to_string(),
));
}
if let Some(sort) = &self.sort {
params.push(("sort".to_string(), sort.to_string()));
}
if let Some(start) = &self.start {
params.push(("start".to_string(), start.to_string()));
}
params
}
pub fn c2coff(&mut self, c2coff: C2COff) -> &mut Self {
self.c2coff = Some(c2coff);
self
}
pub fn cr(&mut self, cr: Box<Cr>) -> &mut Self {
self.cr = Some(cr);
self
}
pub fn cx(&mut self, cx: impl AsRef<str>) -> &mut Self {
self.cx = Some(cx.as_ref().to_string());
self
}
pub fn key(&mut self, key: impl AsRef<str>) -> &mut Self {
self.key = Some(key.as_ref().to_string());
self
}
pub fn date_restrict(&mut self, date_restrict: String) -> &mut Self {
self.date_restrict = Some(date_restrict);
self
}
pub fn exact_terms(&mut self, exact_terms: impl AsRef<str>) -> &mut Self {
self.exact_terms = Some(exact_terms.as_ref().to_string());
self
}
pub fn exclude_terms(&mut self, exclude_terms: impl AsRef<str>) -> &mut Self {
self.exclude_terms = Some(exclude_terms.as_ref().to_string());
self
}
pub fn file_type(&mut self, file_type: String) -> &mut Self {
self.file_type = Some(file_type);
self
}
pub fn filter(&mut self, filter: DuplicateContentFilter) -> &mut Self {
self.filter = Some(filter);
self
}
pub fn gl(&mut self, gl: Gl) -> &mut Self {
self.gl = Some(gl);
self
}
pub fn high_range(&mut self, high_range: String) -> &mut Self {
self.high_range = Some(high_range);
self
}
pub fn hl(&mut self, hl: String) -> &mut Self {
self.hl = Some(hl);
self
}
pub fn hq(&mut self, hq: String) -> &mut Self {
self.hq = Some(hq);
self
}
pub fn img_color_type(&mut self, img_color_type: ImgColorType) -> &mut Self {
self.img_color_type = Some(img_color_type);
self
}
pub fn img_dominant_color(&mut self, img_dominant_color: ImgDominantColor) -> &mut Self {
self.img_dominant_color = Some(img_dominant_color);
self
}
pub fn img_size(&mut self, img_size: ImgSize) -> &mut Self {
self.img_size = Some(img_size);
self
}
pub fn img_type(&mut self, img_type: ImgType) -> &mut Self {
self.img_type = Some(img_type);
self
}
pub fn link_site(&mut self, link_site: String) -> &mut Self {
self.link_site = Some(link_site);
self
}
pub fn lr(&mut self, lr: Lr) -> &mut Self {
self.lr = Some(lr);
self
}
pub fn num(&mut self, num: SearchResultNumber) -> &mut Self {
self.num = Some(num);
self
}
pub fn or_terms(&mut self, or_terms: String) -> &mut Self {
self.or_terms = Some(or_terms);
self
}
pub fn q(&mut self, q: impl AsRef<str>) -> &mut Self {
self.q = Some(q.as_ref().to_string());
self
}
pub fn related_site(&mut self, related_site: String) -> &mut Self {
self.related_site = Some(related_site);
self
}
pub fn rights(&mut self, rights: Rights) -> &mut Self {
self.rights = Some(Box::new(rights));
self
}
pub fn safe(&mut self, safe: Safe) -> &mut Self {
self.safe = Some(safe);
self
}
pub fn search_type(&mut self, search_type: SearchType) -> &mut Self {
self.search_type = Some(search_type);
self
}
pub fn site_search(&mut self, site_search: String) -> &mut Self {
self.site_search = Some(site_search);
self
}
pub fn site_search_filter(&mut self, site_search_filter: SiteSearchFilter) -> &mut Self {
self.site_search_filter = Some(site_search_filter);
self
}
pub fn sort(&mut self, sort: String) -> &mut Self {
self.sort = Some(sort);
self
}
pub fn start(&mut self, start: u32) -> &mut Self {
self.start = Some(start);
self
}
pub fn next_page(&mut self, n: NextPage) -> &mut Self {
self.start = Some(n.start);
self
}
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Item {
pub title: String,
pub link: String,
pub snippet: String,
}
pub struct NextPage {
pub start: u32,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct SearchResults {
pub items: Vec<Item>,
#[serde(flatten)]
other: Map<String, Value>,
}
impl SearchResults {
pub fn next_page(&self) -> Option<NextPage> {
self.other
.get("queries")
.and_then(|queries| queries.get("nextPage"))
.and_then(|next_page| next_page.get(0))
.and_then(|next_page| next_page.get("startIndex"))
.and_then(|start_index| start_index.as_u64())
.map(|start_index| NextPage {
start: start_index as u32,
})
}
}