use std::path::Path;
pub fn read_state(state_path: &Path) -> Option<serde_json::Value> {
let text = std::fs::read_to_string(state_path).ok()?;
serde_json::from_str(&text).ok()
}
pub fn adapter_to_camel(a: &serde_json::Map<String, serde_json::Value>) -> serde_json::Value {
use serde_json::Value;
let mut out = serde_json::Map::new();
for key in ["name", "vidpid"] {
out.insert(key.to_string(), a.get(key).cloned().unwrap_or(Value::Null));
}
out.insert(
"usbPath".to_string(),
a.get("usb_path").cloned().unwrap_or(Value::Null),
);
out.insert(
"state".to_string(),
a.get("state").cloned().unwrap_or(Value::Null),
);
let applied_live = a.get("applied_live").map(value_is_truthy).unwrap_or(false);
out.insert("appliedLive".to_string(), Value::Bool(applied_live));
for (src, dst) in [
("source", "source"),
("pinned_mac", "pinnedMac"),
("last_seen_mac", "lastSeenMac"),
("link_file", "linkFile"),
("deferred_reason", "deferredReason"),
] {
if let Some(v) = a.get(src) {
if !v.is_null() {
out.insert(dst.to_string(), v.clone());
}
}
}
Value::Object(out)
}
fn value_is_truthy(v: &serde_json::Value) -> bool {
use serde_json::Value;
match v {
Value::Null => false,
Value::Bool(b) => *b,
Value::Number(n) => n.as_f64().map(|f| f != 0.0).unwrap_or(true),
Value::String(s) => !s.is_empty(),
Value::Array(a) => !a.is_empty(),
Value::Object(o) => !o.is_empty(),
}
}
pub fn state_json_to_camel(raw: Option<&serde_json::Value>) -> Vec<serde_json::Value> {
raw.and_then(|d| d.get("adapters"))
.and_then(serde_json::Value::as_array)
.map(|arr| {
arr.iter()
.filter_map(serde_json::Value::as_object)
.map(adapter_to_camel)
.collect()
})
.unwrap_or_default()
}
pub fn adapters_camel_from(state_path: &Path) -> Option<serde_json::Value> {
let raw = read_state(state_path);
let adapters = state_json_to_camel(raw.as_ref());
if adapters.is_empty() {
return None;
}
Some(serde_json::json!({ "adapters": adapters }))
}