use crate::error::{ModelError, Result};
use crate::format::{CharFormat, CharValue};
use serde::Deserialize;
use std::fmt::Write as _;
#[derive(Debug, Clone, PartialEq)]
pub struct CharRead {
pub aid: u64,
pub iid: u64,
pub value: Option<CharValue>,
pub status: Option<i64>,
}
pub fn build_read_request(ids: &[(u64, u64)]) -> String {
let mut path = String::from("/characteristics?id=");
for (i, (aid, iid)) in ids.iter().enumerate() {
if i > 0 {
path.push(',');
}
let _ = write!(path, "{aid}.{iid}");
}
path.push_str("&meta=1");
path
}
#[derive(Debug, Deserialize)]
struct WireReadResponse {
characteristics: Vec<WireReadEntry>,
}
#[derive(Debug, Deserialize)]
struct WireReadEntry {
aid: u64,
iid: u64,
#[serde(default)]
value: Option<serde_json::Value>,
#[serde(default)]
status: Option<i64>,
#[serde(default)]
format: Option<CharFormat>,
}
pub fn parse_read_response(json: &[u8]) -> Result<Vec<((u64, u64), CharValue)>> {
let wire: WireReadResponse = serde_json::from_slice(json)?;
let mut out = Vec::with_capacity(wire.characteristics.len());
for e in wire.characteristics {
if let Some(s) = e.status {
if s != 0 {
return Err(ModelError::CharacteristicStatus {
aid: e.aid,
iid: e.iid,
status: s,
});
}
}
let Some(raw) = e.value else { continue };
let value = match e.format {
Some(fmt) => fmt.value_from_json(&raw)?,
None => infer_value(&raw)?,
};
out.push(((e.aid, e.iid), value));
}
Ok(out)
}
fn infer_value(v: &serde_json::Value) -> Result<CharValue> {
use serde_json::Value as J;
match v {
J::Bool(b) => Ok(CharValue::Bool(*b)),
J::String(s) => Ok(CharValue::Str(s.clone())),
J::Number(n) => {
if let Some(u) = n.as_u64() {
Ok(CharValue::Uint(u))
} else if let Some(i) = n.as_i64() {
Ok(CharValue::Int(i))
} else if let Some(f) = n.as_f64() {
Ok(CharValue::Float(f))
} else {
Err(ModelError::ValueType {
format: "unknown",
detail: format!("uninterpretable number {n}"),
})
}
}
other => Err(ModelError::ValueType {
format: "unknown",
detail: format!("got JSON {other}"),
}),
}
}
pub fn build_write_request(writes: &[((u64, u64), CharValue)]) -> Vec<u8> {
let entries: Vec<serde_json::Value> = writes
.iter()
.map(|((aid, iid), v)| serde_json::json!({ "aid": aid, "iid": iid, "value": v.to_json() }))
.collect();
let body = serde_json::json!({ "characteristics": entries });
serde_json::to_vec(&body).unwrap_or_default()
}
pub fn build_subscribe_request(ids: &[(u64, u64)], enable: bool) -> Vec<u8> {
let entries: Vec<serde_json::Value> = ids
.iter()
.map(|(aid, iid)| serde_json::json!({ "aid": aid, "iid": iid, "ev": enable }))
.collect();
let body = serde_json::json!({ "characteristics": entries });
serde_json::to_vec(&body).unwrap_or_default()
}