use serde::{Deserialize, Serialize};
use std::cell::RefCell;
pub use crate::extension::*;
pub struct Host;
impl Host {
pub fn http_request(method: &str, url: &str) -> Result<serde_json::Value, String> {
super::bindings::http_request(method, url)
}
pub fn log(level: &str, message: &str) {
super::bindings::log(level, message)
}
pub fn store_metric(name: &str, value: &serde_json::Value) {
super::bindings::store_metric(name, value)
}
pub fn device_read(device_id: &str, metric: &str) -> Result<serde_json::Value, String> {
super::bindings::device_read(device_id, metric)
}
pub fn device_write(
device_id: &str,
command: &str,
params: &serde_json::Value,
) -> Result<serde_json::Value, String> {
super::bindings::device_write(device_id, command, params)
}
}
thread_local! {
static WASM_METRIC_CACHE: RefCell<std::collections::HashMap<String, SdkMetricValue>> =
RefCell::new(std::collections::HashMap::new());
}
pub fn store_metric_value<T: Into<SdkMetricValue>>(name: &str, value: T) {
let metric_value = value.into();
WASM_METRIC_CACHE.with(|cache| {
cache.borrow_mut().insert(name.to_string(), metric_value);
});
}
pub fn init_metric_cache() {
}
pub fn clear_metric_cache() {
WASM_METRIC_CACHE.with(|cache| {
cache.borrow_mut().clear();
});
}
pub fn get_cached_metrics_json() -> String {
WASM_METRIC_CACHE.with(|cache| {
let cache = cache.borrow();
let metrics: Vec<_> = cache
.iter()
.map(|(k, v)| {
serde_json::json!({
"name": k,
"value": v,
"timestamp": 0
})
})
.collect();
serde_json::to_string(&metrics).unwrap_or_else(|_| "[]".to_string())
})
}
pub fn cached_metrics_count() -> usize {
WASM_METRIC_CACHE.with(|cache| cache.borrow().len())
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_store_metric_value_string() {
clear_metric_cache();
store_metric_value("test_string", SdkMetricValue::String("hello".to_string()));
assert_eq!(cached_metrics_count(), 1);
}
#[test]
fn test_store_metric_value_float() {
clear_metric_cache();
store_metric_value("test_float", SdkMetricValue::Float(42.5));
assert_eq!(cached_metrics_count(), 1);
}
#[test]
fn test_store_metric_value_int() {
clear_metric_cache();
store_metric_value("test_int", SdkMetricValue::Int(100));
assert_eq!(cached_metrics_count(), 1);
}
#[test]
fn test_store_metric_value_bool() {
clear_metric_cache();
store_metric_value("test_bool", SdkMetricValue::Bool(true));
assert_eq!(cached_metrics_count(), 1);
}
#[test]
fn test_clear_metric_cache() {
clear_metric_cache();
store_metric_value("metric1", SdkMetricValue::Int(1));
store_metric_value("metric2", SdkMetricValue::Int(2));
assert_eq!(cached_metrics_count(), 2);
clear_metric_cache();
assert_eq!(cached_metrics_count(), 0);
}
#[test]
fn test_get_cached_metrics_json_empty() {
clear_metric_cache();
let json = get_cached_metrics_json();
assert_eq!(json, "[]");
}
#[test]
fn test_get_cached_metrics_json_with_data() {
clear_metric_cache();
store_metric_value("temperature", SdkMetricValue::Float(25.5));
store_metric_value("humidity", SdkMetricValue::Float(65.0));
let json = get_cached_metrics_json();
let parsed: Vec<serde_json::Value> = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.len(), 2);
let temp_metric = parsed.iter().find(|m| m["name"] == "temperature").unwrap();
assert_eq!(temp_metric["value"]["Float"], 25.5);
let humidity_metric = parsed.iter().find(|m| m["name"] == "humidity").unwrap();
assert_eq!(humidity_metric["value"]["Float"], 65.0);
}
#[test]
fn test_metric_cache_overwrite() {
clear_metric_cache();
store_metric_value("metric", SdkMetricValue::Int(10));
assert_eq!(cached_metrics_count(), 1);
store_metric_value("metric", SdkMetricValue::Int(20));
assert_eq!(cached_metrics_count(), 1);
let json = get_cached_metrics_json();
let parsed: Vec<serde_json::Value> = serde_json::from_str(&json).unwrap();
assert_eq!(parsed[0]["value"]["Int"], 20);
}
#[test]
fn test_init_metric_cache_noop() {
init_metric_cache();
init_metric_cache();
}
#[test]
fn test_sdk_metric_value_serialization() {
let float_val = SdkMetricValue::Float(3.14);
let json = serde_json::to_string(&float_val).unwrap();
assert!(json.contains("Float"));
assert!(json.contains("3.14"));
let int_val = SdkMetricValue::Int(42);
let json = serde_json::to_string(&int_val).unwrap();
assert!(json.contains("Int"));
assert!(json.contains("42"));
let string_val = SdkMetricValue::String("test".to_string());
let json = serde_json::to_string(&string_val).unwrap();
assert!(json.contains("String"));
assert!(json.contains("test"));
let bool_val = SdkMetricValue::Bool(true);
let json = serde_json::to_string(&bool_val).unwrap();
assert!(json.contains("Bool"));
assert!(json.contains("true"));
}
#[test]
fn test_sdk_metric_value_deserialization() {
let json = r#"{"Float":2.718}"#;
let value: SdkMetricValue = serde_json::from_str(json).unwrap();
assert!(matches!(value, SdkMetricValue::Float(v) if (v - 2.718).abs() < 0.001));
let json = r#"{"Int":100}"#;
let value: SdkMetricValue = serde_json::from_str(json).unwrap();
assert!(matches!(value, SdkMetricValue::Int(100)));
let json = r#"{"String":"hello"}"#;
let value: SdkMetricValue = serde_json::from_str(json).unwrap();
assert!(matches!(value, SdkMetricValue::String(s) if s == "hello"));
let json = r#"{"Bool":false}"#;
let value: SdkMetricValue = serde_json::from_str(json).unwrap();
assert!(matches!(value, SdkMetricValue::Bool(false)));
}
#[test]
fn test_host_static_methods() {
let _ = Host::http_request("GET", "http://example.com");
Host::log("info", "test message");
let _ = Host::device_read("device-1", "temperature");
let _ = Host::device_write("device-1", "set_level", &json!({"level": 80}));
Host::store_metric("virtual_metric", &json!(42));
}
#[test]
fn test_multiple_metrics_storage() {
clear_metric_cache();
store_metric_value("counter", SdkMetricValue::Int(100));
store_metric_value("gauge", SdkMetricValue::Float(3.14159));
store_metric_value("label", SdkMetricValue::String("active".to_string()));
store_metric_value("flag", SdkMetricValue::Bool(true));
assert_eq!(cached_metrics_count(), 4);
let json = get_cached_metrics_json();
let parsed: Vec<serde_json::Value> = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.len(), 4);
}
#[test]
fn test_metric_value_into_conversion() {
let value: SdkMetricValue = 42i64.into();
assert!(matches!(value, SdkMetricValue::Int(42)));
let value: SdkMetricValue = 3.14f64.into();
assert!(matches!(value, SdkMetricValue::Float(v) if (v - 3.14).abs() < 0.001));
let value: SdkMetricValue = true.into();
assert!(matches!(value, SdkMetricValue::Bool(true)));
let value: SdkMetricValue = "test".to_string().into();
assert!(matches!(value, SdkMetricValue::String(s) if s == "test"));
}
}