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}