m4a_agent/backend/
wire.rs1use serde_json::Value;
5
6use crate::error::{AgentError, Result};
7
8pub trait Wire: Send + Sync {
9 fn post(&self, path: &str, body: &Value, bearer: Option<&str>) -> Result<(u16, Value)>;
11 fn get(&self, path: &str, bearer: Option<&str>) -> Result<(u16, Value)>;
12}
13
14#[cfg(any(feature = "tier-server", feature = "tier-matrix"))]
15pub struct HttpWire {
16 base: String,
17 http: reqwest::blocking::Client,
18}
19
20#[cfg(any(feature = "tier-server", feature = "tier-matrix"))]
21impl HttpWire {
22 pub fn new(base: &str) -> Result<Self> {
23 let http = reqwest::blocking::Client::builder().timeout(std::time::Duration::from_secs(30)).build().map_err(|e| AgentError::Transport(e.to_string()))?;
24 Ok(Self { base: base.trim_end_matches('/').to_string(), http })
25 }
26 fn finish(r: reqwest::Result<reqwest::blocking::Response>) -> Result<(u16, Value)> {
27 let r = r.map_err(|e| AgentError::Transport(e.to_string()))?;
28 let status = r.status().as_u16();
29 Ok((status, r.json::<Value>().unwrap_or(Value::Null)))
30 }
31}
32
33#[cfg(any(feature = "tier-server", feature = "tier-matrix"))]
34impl Wire for HttpWire {
35 fn post(&self, path: &str, body: &Value, bearer: Option<&str>) -> Result<(u16, Value)> {
36 let mut r = self.http.post(format!("{}{path}", self.base)).json(body);
37 if let Some(b) = bearer {
38 r = r.bearer_auth(b);
39 }
40 Self::finish(r.send())
41 }
42 fn get(&self, path: &str, bearer: Option<&str>) -> Result<(u16, Value)> {
43 let mut r = self.http.get(format!("{}{path}", self.base));
44 if let Some(b) = bearer {
45 r = r.bearer_auth(b);
46 }
47 Self::finish(r.send())
48 }
49}
50
51pub fn field(v: &Value, key: &str) -> Result<String> {
53 v.get(key).and_then(Value::as_str).map(str::to_string).ok_or_else(|| AgentError::Protocol(format!("answer without {key}")))
54}