duckduckgo-core 0.1.6

DuckDuckGo search client library for duckduckgo-cli
Documentation
use std::path::PathBuf;
use std::sync::Arc;

use crate::clock::{SharedClock, SystemClock};
use crate::rate_limit::{Limits, ProgressHook};
use crate::region::Region;

#[derive(Clone)]
pub(crate) struct ClientOptions {
    pub endpoint: String,
    pub region: Region,
    pub num: usize,
    pub safe: bool,
    pub timeout: u64,
    pub proxy: Option<String>,
    pub user_agent: Option<String>,
    pub retry: u8,
    pub no_wait: bool,
    pub no_rate_limit: bool,
    pub state_dir: PathBuf,
    pub limits: Limits,
    pub clock: SharedClock,
    pub progress_hook: Option<ProgressHook>,
}

impl std::fmt::Debug for ClientOptions {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("ClientOptions")
            .field("endpoint", &self.endpoint)
            .field("region", &self.region)
            .field("num", &self.num)
            .field("safe", &self.safe)
            .field("timeout", &self.timeout)
            .field("proxy", &self.proxy)
            .field("user_agent", &self.user_agent)
            .field("retry", &self.retry)
            .field("no_wait", &self.no_wait)
            .field("no_rate_limit", &self.no_rate_limit)
            .field("state_dir", &self.state_dir)
            .field("limits", &self.limits)
            .field("clock", &"<clock>")
            .field(
                "progress_hook",
                &self.progress_hook.as_ref().map(|_| "<hook>"),
            )
            .finish()
    }
}

impl Default for ClientOptions {
    fn default() -> Self {
        Self {
            // ddgr --noua posts to this endpoint without a trailing slash.
            endpoint: "https://html.duckduckgo.com/html".to_owned(),
            region: Region::default(),
            num: 10,
            safe: true,
            timeout: 30,
            proxy: None,
            user_agent: None,
            retry: 3,
            no_wait: false,
            no_rate_limit: false,
            state_dir: PathBuf::from("."),
            limits: Limits::from_env(),
            clock: Arc::new(SystemClock),
            progress_hook: None,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::ClientOptions;
    use std::path::PathBuf;

    #[test]
    fn defaults_match_public_cli_contract() {
        let options = ClientOptions::default();
        assert_eq!(options.endpoint, "https://html.duckduckgo.com/html");
        assert_eq!(options.region.code(), "us-en");
        assert_eq!(options.num, 10);
        assert!(options.safe);
        assert_eq!(options.timeout, 30);
        assert_eq!(options.retry, 3);
        assert_eq!(options.state_dir, PathBuf::from("."));
    }

    #[test]
    fn debug_redacts_non_debuggable_hooks_and_clock() {
        let options = ClientOptions::default();
        let debug = format!("{options:?}");
        assert!(debug.contains("endpoint"));
        assert!(debug.contains("<clock>"));
        assert!(debug.contains("progress_hook"));
    }
}