Skip to main content

socorro_cli/
client.rs

1use crate::models::{ProcessedCrash, SearchParams, SearchResponse};
2use crate::{Error, Result};
3use reqwest::blocking::Client;
4use reqwest::StatusCode;
5
6pub struct SocorroClient {
7    base_url: String,
8    client: Client,
9}
10
11impl SocorroClient {
12    pub fn new(base_url: String) -> Self {
13        Self {
14            base_url,
15            client: Client::new(),
16        }
17    }
18
19    pub fn get_crash(&self, crash_id: &str) -> Result<ProcessedCrash> {
20        if !crash_id.chars().all(|c| c.is_ascii_hexdigit() || c == '-') {
21            return Err(Error::InvalidCrashId(crash_id.to_string()));
22        }
23
24        let url = format!("{}/ProcessedCrash/", self.base_url);
25        let request = self.client.get(&url).query(&[("crash_id", crash_id)]);
26
27        let response = request.send()?;
28
29        match response.status() {
30            StatusCode::OK => {
31                let text = response.text()?;
32                serde_json::from_str(&text).map_err(|e| {
33                    Error::ParseError(format!("{}: {}", e, &text[..text.len().min(200)]))
34                })
35            }
36            StatusCode::NOT_FOUND => Err(Error::NotFound(crash_id.to_string())),
37            StatusCode::TOO_MANY_REQUESTS => Err(Error::RateLimited),
38            _ => Err(Error::Http(reqwest::Error::from(
39                response.error_for_status().unwrap_err(),
40            ))),
41        }
42    }
43
44    pub fn search(&self, params: SearchParams) -> Result<SearchResponse> {
45        let url = format!("{}/SuperSearch/", self.base_url);
46
47        let mut query_params = vec![
48            ("product", params.product),
49            ("_results_number", params.limit.to_string()),
50            ("_sort", params.sort),
51        ];
52
53        let days_ago = chrono::Utc::now() - chrono::Duration::days(params.days as i64);
54        query_params.push(("date", format!(">={}", days_ago.format("%Y-%m-%d"))));
55
56        if let Some(sig) = params.signature {
57            query_params.push(("signature", sig));
58        }
59
60        if let Some(ver) = params.version {
61            query_params.push(("version", ver));
62        }
63
64        if let Some(plat) = params.platform {
65            query_params.push(("os_name", plat));
66        }
67
68        for facet in params.facets {
69            query_params.push(("_facets", facet));
70        }
71
72        let mut request = self.client.get(&url);
73        for (key, value) in query_params {
74            request = request.query(&[(key, value)]);
75        }
76
77        let response = request.send()?;
78
79        match response.status() {
80            StatusCode::OK => {
81                let text = response.text()?;
82                serde_json::from_str(&text).map_err(|e| {
83                    Error::ParseError(format!("{}: {}", e, &text[..text.len().min(200)]))
84                })
85            }
86            StatusCode::TOO_MANY_REQUESTS => Err(Error::RateLimited),
87            _ => Err(Error::Http(response.error_for_status().unwrap_err())),
88        }
89    }
90}