use serde_json;
use serde_json::json;
use std::marker::{Send, Sync};
use valico::json_schema;
pub trait ValueForwarder: Send + Sync {
fn set_value(&mut self, value: serde_json::Value) -> Result<serde_json::Value, &'static str>;
}
pub trait Property: Send + Sync {
fn validate_value(&self, value: &serde_json::Value) -> Result<(), &'static str> {
let mut description = self.get_metadata();
description.remove("@type");
description.remove("unit");
description.remove("title");
if description
.get("readOnly")
.and_then(|b| b.as_bool())
.unwrap_or(false)
{
return Err("Read-only property");
}
let mut scope = json_schema::Scope::new();
match scope.compile_and_return(json!(description), true) {
Ok(validator) => {
if validator.validate(value).is_valid() {
Ok(())
} else {
Err("Invalid property value")
}
}
Err(_) => Err("Invalid property schema"),
}
}
fn as_property_description(&self) -> serde_json::Map<String, serde_json::Value> {
let mut description = self.get_metadata();
let link = json!(
{
"rel": "property",
"href": self.get_href(),
}
);
if let Some(links) = description
.get_mut("links")
.map(|links| links.as_array_mut().unwrap())
{
links.push(link);
} else {
description.insert("links".to_string(), json!([link]));
}
description
}
fn set_href_prefix(&mut self, prefix: String);
fn get_href(&self) -> String;
fn get_value(&self) -> serde_json::Value;
fn set_value(&mut self, value: serde_json::Value) -> Result<(), &'static str>;
fn set_cached_value(&mut self, value: serde_json::Value) -> Result<(), &'static str>;
fn get_name(&self) -> String;
fn get_metadata(&self) -> serde_json::Map<String, serde_json::Value>;
}
pub struct BaseProperty {
name: String,
last_value: serde_json::Value,
value_forwarder: Option<Box<dyn ValueForwarder>>,
href_prefix: String,
href: String,
metadata: serde_json::Map<String, serde_json::Value>,
}
impl BaseProperty {
pub fn new(
name: String,
initial_value: serde_json::Value,
value_forwarder: Option<Box<dyn ValueForwarder>>,
metadata: Option<serde_json::Map<String, serde_json::Value>>,
) -> BaseProperty {
let meta = match metadata {
Some(m) => m,
None => serde_json::Map::new(),
};
let href = format!("/properties/{}", name);
BaseProperty {
name,
last_value: initial_value,
value_forwarder,
href_prefix: "".to_owned(),
href,
metadata: meta,
}
}
}
impl Property for BaseProperty {
fn set_href_prefix(&mut self, prefix: String) {
self.href_prefix = prefix;
}
fn get_href(&self) -> String {
format!("{}{}", self.href_prefix, self.href)
}
fn get_value(&self) -> serde_json::Value {
self.last_value.clone()
}
fn set_value(&mut self, value: serde_json::Value) -> Result<(), &'static str> {
self.validate_value(&value)?;
match self.value_forwarder {
Some(ref mut vf) => match vf.set_value(value) {
Ok(v) => {
self.last_value = v;
Ok(())
}
Err(e) => Err(e),
},
None => {
self.last_value = value;
Ok(())
}
}
}
fn set_cached_value(&mut self, value: serde_json::Value) -> Result<(), &'static str> {
self.last_value = value;
Ok(())
}
fn get_name(&self) -> String {
self.name.clone()
}
fn get_metadata(&self) -> serde_json::Map<String, serde_json::Value> {
self.metadata.clone()
}
}