File size: 1,326 Bytes
3d2a871
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
use anyhow::Result;
use serde::Deserialize;

#[derive(Deserialize, Debug)]
struct DdgResult {
    #[serde(rename = "Text")]
    text:     Option<String>,
    #[serde(rename = "FirstURL")]
    first_url: Option<String>,
}

#[derive(Deserialize, Debug)]
struct DdgResponse {
    #[serde(rename = "AbstractText")]
    abstract_text: Option<String>,
    #[serde(rename = "RelatedTopics")]
    related_topics: Vec<DdgResult>,
}

pub async fn search(client: &reqwest::Client, query: &str) -> Result<String> {
    let url = format!(
        "https://api.duckduckgo.com/?q={}&format=json&no_html=1&skip_disambig=1",
        urlencoding::encode(query)
    );
    let resp = client
        .get(&url)
        .header("User-Agent", "sovereign-35/1.0")
        .send()
        .await?
        .json::<DdgResponse>()
        .await?;

    let mut out = String::new();
    if let Some(abs) = &resp.abstract_text {
        if !abs.is_empty() {
            out.push_str(&format!("DDG ABSTRACT: {}\n\n", abs));
        }
    }
    for (i, r) in resp.related_topics.iter().take(3).enumerate() {
        if let (Some(text), Some(url)) = (&r.text, &r.first_url) {
            out.push_str(&format!("[DDG {}] {}\n{}\n\n", i + 1, text, url));
        }
    }
    if out.is_empty() {
        out = "DDG: no results".to_string();
    }
    Ok(out)
}