DataSourceConfig refactored to enum: External/Clock/StaticText. Clock generates formatted time via chrono, static text emits configured string. ESP32: connection status indicator (green/red dot bottom-right), per-widget clear before redraw, RenderEvent enum for local + server messages. Polling uses DataUpdate instead of ScreenUpdate to avoid wiping widget state. Empty mappings passthrough raw source data for internal sources.
76 lines
2.0 KiB
Rust
76 lines
2.0 KiB
Rust
use domain::{DataSource, DataSourcePort, Value};
|
|
|
|
pub struct HttpJsonAdapter {
|
|
client: reqwest::Client,
|
|
}
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
pub enum HttpJsonError {
|
|
#[error("request: {0}")]
|
|
Request(#[from] reqwest::Error),
|
|
#[error("no url configured")]
|
|
NoUrl,
|
|
#[error("parse: {0}")]
|
|
Parse(String),
|
|
}
|
|
|
|
impl Default for HttpJsonAdapter {
|
|
fn default() -> Self {
|
|
Self {
|
|
client: reqwest::Client::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl HttpJsonAdapter {
|
|
pub fn new() -> Self {
|
|
Self::default()
|
|
}
|
|
}
|
|
|
|
fn json_to_value(json: serde_json::Value) -> Value {
|
|
match json {
|
|
serde_json::Value::Null => Value::Null,
|
|
serde_json::Value::Bool(b) => Value::Bool(b),
|
|
serde_json::Value::Number(n) => Value::Number(n.as_f64().unwrap_or(0.0)),
|
|
serde_json::Value::String(s) => Value::String(s),
|
|
serde_json::Value::Array(arr) => Value::Array(arr.into_iter().map(json_to_value).collect()),
|
|
serde_json::Value::Object(map) => Value::Object(
|
|
map.into_iter()
|
|
.map(|(k, v)| (k, json_to_value(v)))
|
|
.collect(),
|
|
),
|
|
}
|
|
}
|
|
|
|
impl DataSourcePort for HttpJsonAdapter {
|
|
type Error = HttpJsonError;
|
|
|
|
async fn poll(&self, source: &DataSource) -> Result<Value, Self::Error> {
|
|
let domain::DataSourceConfig::External {
|
|
ref url,
|
|
ref headers,
|
|
ref api_key,
|
|
} = source.config
|
|
else {
|
|
return Err(HttpJsonError::NoUrl);
|
|
};
|
|
let url = url.as_ref().ok_or(HttpJsonError::NoUrl)?;
|
|
|
|
let mut req = self.client.get(url);
|
|
|
|
for (key, val) in headers {
|
|
req = req.header(key, val);
|
|
}
|
|
|
|
if let Some(api_key) = api_key {
|
|
req = req.header("Authorization", format!("Bearer {api_key}"));
|
|
}
|
|
|
|
let resp = req.send().await.map_err(HttpJsonError::Request)?;
|
|
let json: serde_json::Value = resp.json().await.map_err(HttpJsonError::Request)?;
|
|
|
|
Ok(json_to_value(json))
|
|
}
|
|
}
|