Skip to main content

conduit_cli/core/modrinth/
search.rs

1use super::client::ModrinthAPI;
2use super::models::SearchResult;
3use reqwest::Url;
4
5impl ModrinthAPI {
6    pub async fn search(
7        &self,
8        query: &str,
9        limit: i32,
10        offset: i32,
11        index: &str,
12        facets: Option<String>,
13    ) -> Result<SearchResult, reqwest::Error> {
14        let mod_filter = "\"project_type:mod\"";
15
16        let final_facets = match facets {
17            Some(f) => {
18                if f.starts_with('[') && f.ends_with(']') {
19                    let inner = &f[1..f.len() - 1];
20                    format!("[{inner},[{mod_filter}]]")
21                } else {
22                    format!("[[{mod_filter}]]")
23                }
24            }
25            None => format!("[[{mod_filter}]]"),
26        };
27
28        let params = vec![
29            ("query", query.to_string()),
30            ("limit", limit.to_string()),
31            ("offset", offset.to_string()),
32            ("index", index.to_string()),
33            ("facets", final_facets),
34        ];
35
36        let url = Url::parse_with_params(&format!("{}/search", self.base_url), &params)
37            .expect("Critical: Failed to build Modrinth search URL");
38
39        self.client
40            .get(url)
41            .send()
42            .await?
43            .error_for_status()?
44            .json::<SearchResult>()
45            .await
46    }
47
48    pub async fn get_suggestions(&self, input: &str) -> Vec<(String, String)> {
49        let query = input.split('@').next().unwrap_or(input);
50
51        match self.search(query, 5, 0, "relevance", None).await {
52            Ok(results) => results
53                .hits
54                .into_iter()
55                .map(|hit| (hit.title, hit.slug))
56                .collect(),
57            Err(_) => Vec::new(),
58        }
59    }
60}