bing-search 2.0.3

Bing search engine in your terminal
use std::{vec};

use clap::Parser;
use reqwest;
use soup::prelude::*;
use std::process::Command;
mod cli;
use reqwest::Client;
use std::fs::File;
use std::io::copy;

/// # Bing Search
///
/// [search keywords](https://support.microsoft.com/en-gb/topic/advanced-search-keywords-ea595928-5d63-4a0b-9c6b-0b769865e78a)
///
/// [advanced search options](https://support.microsoft.com/en-gb/topic/advanced-search-options-b92e25f1-0085-4271-bdf9-14aaea720930)
#[tokio::main]
async fn main() -> Result<(), String> {
    let cli = cli::Cli::parse();

    let query = cli.query;
    let image = cli.image;
    if image {
        // check if os is windows
        // if windows, print it does not support images
        if cfg!(windows) {
            println!("Windows does not support images!");
            return Ok(());
        }
        println!("Image search");
    } else {
        println!("Text search");
    }
    println!("Query: {}", query);
    if image {
        Command::new("command")
            .arg("-v")
            .arg("viu")
            .spawn()
            .expect("You don't have viu, you can't view images! Install it! cargo install viu or https://github.com/atanunq/viu");
    }
    let max_results = cli.max.unwrap_or(10);

    let cvid = cli
        .cvid
        .unwrap_or(String::from("4D2EA03FB1D5439C994D1F5C7D902272"));

    let mut query_num = 1;
    loop {
        let test = query_num - 1;
        if test > max_results {
            break;
        }
        let _user_agent = "{'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'}";
        let client = reqwest::Client::builder().user_agent("bing-search").build();
        let bing: String;
        if image {
            bing = format!(
                "https://www.bing.com/images/search?q={}&go=Search&qs=ds&form=QBIR&first={}",
                query, query_num
            );
        } else {
            bing = format!("https://www.bing.com/search?q={}&qs=n&form=QBRE&sp=-1&sc=8-9&sk=&cvid={}&setlang=en&first={}", query, cvid, query_num);
        }
        let c = client.expect("Internal error, cal 911!");
        let resp = c.get(bing).send().await.unwrap().text().await;
        let soup = Soup::new(resp.unwrap().as_str());
        if image {
            // image query
            //let div_top = soup.class("mimg").find_all();
            let img_mid = soup.class("dgControl_list").find_all();
            for div in img_mid {
                let div = div.tag("img").find_all();
                for i in div {
                    let copy1 = i.clone();
                    let copy2 = i.clone();
                    let copy3 = i.clone();
                    let copy4 = i.clone();
                    let copy5 = i.clone();
                    let description = i.parent()
                        .unwrap_or(copy1)
                        .parent()
                        .unwrap_or(copy2)
                        .parent()
                        .unwrap_or(copy3)
                        .parent()
                        .unwrap_or(copy4)
                        .class("inflnk")
                        .find()
                        .unwrap_or(copy5)
                        .get("aria-label");
                    let href = &i.parent().unwrap().parent().unwrap().get("href");
                    let src = i.get("src");
                    match src {
                        Some(src) => {
                            let tmp_file = "/tmp/bing-search-temporary-image";
                            let _res = download_image(src.as_str(), tmp_file).await;
                            let mut viu = Command::new("viu")
                                .arg(tmp_file)
                                .stderr(std::process::Stdio::inherit())
                                .spawn()
                                .expect("Failed to display image!");
                            // create variable for stderr that display it's text
                            // if it does not contain any string, then it is successful
                            // if it contains a string, then it is not successful
                            // if it is not successful, then it will print the error
                            let viu_end = viu.wait().expect("Failed to display image!");
                            let viu_status = viu_end.success();
                            if viu_status {
                                println!("Displayed image!");
                            } else {
                                println!("Failed to display image!");
                            }
                            match href {
                                Some(href) => {
                                    if viu_status {
                                        println!("Description: {}", description.unwrap());
                                        println!("https://bing.com{}", href);
                                    }
                                }
                                None => {}
                            }
                            //let viu_status = viu.stderr.unwrap(). is_empty("Failed to display image!");
                            let rm = Command::new("rm")
                                .arg(tmp_file)
                                .output()
                                .expect("Failed to remove image!");
                            let rm_status = rm.status.success();
                            if rm_status {
                                println!("Removed temporary image!");
                            } else {
                                println!("Failed to remove temporary image!");
                            }
                        }
                        _ => {}
                    }
                }
            }
        } else {
            // text query
            let ol_opt = soup.tag("ol").find();
            let ol = ol_opt.expect("No results returned!");
            let li = ol.tag("li").find_all();
            let mut tmp_s_v: Vec<String> = vec![];
            li.for_each(|l| {
                let children = l.children();
                for (_, child) in children.enumerate() {
                    let h2 = child.tag("h2").find_all();
                    for (_, h) in h2.enumerate() {
                        let ha = h.tag("a").find();
                        match ha {
                            Some(ha) => {
                                let href = ha.get("href");
                                match href {
                                    Some(href) => {
                                        if href.get(0..4) != Some(&"http".to_string()) {
                                            continue;
                                        }
                                        if href.get(0..28)
                                            == Some(&"https://www.bing.com/aclick?".to_string())
                                        {
                                            continue;
                                        }

                                        let mut tmp_s_h = format!("{} - {}\n", href, h.text());
                                        let div_out = child.tag("div").find_all();
                                        let mut tmp_s_p: String = String::from("");
                                        let mut last_p: String = String::from("");
                                        for (_, div) in div_out.enumerate() {
                                            let p = div.tag("p").find();
                                            match p {
                                                Some(p) => {
                                                    let p_t = p.text();
                                                    if last_p == p_t {
                                                        continue;
                                                    }
                                                    last_p = p.text();
                                                    tmp_s_p = format!("{}\n{}\n", tmp_s_p, p_t)
                                                }
                                                _ => {}
                                            }
                                        }
                                        tmp_s_h = format!("{}{}", tmp_s_h, tmp_s_p);
                                        let mut tmp: Vec<String> = vec![tmp_s_h];
                                        tmp_s_v.append(&mut tmp);
                                    }
                                    _ => {}
                                }
                            }
                            _ => {}
                        }
                    }
                }
            });

            tmp_s_v.dedup();
            for s in tmp_s_v.iter() {
                print!("{}", s);
            }

            query_num += 10;
        }
        if image {
            query_num += 10;
        }
    }
    println!("let's go and try another search!");
    return Ok(());
}

async fn download_image(url: &str, path: &str) -> Result<(), reqwest::Error> {
    let client = Client::new();
    let response = client.get(url).send().await?;
    let body = response.bytes().await?;
    let mut dest = File::create(path).expect("Failed to create file");
    copy(&mut &body[..], &mut dest).unwrap();
    Ok(())
}