use serde_json;
use std::marker::{Send, Sync};
use valico::json_schema;
pub trait ValueForwarder: Send + Sync {
fn set_value(&mut self, 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.contains_key("readOnly") {
let b = description.get("readOnly").unwrap().as_bool();
if b.is_some() && b.unwrap() {
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().clone();
let link = json!(
{
"rel": "property",
"href": self.get_href(),
}
);
if description.contains_key("links") {
let links = description
.get_mut("links")
.unwrap()
.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: name,
last_value: initial_value,
value_forwarder: value_forwarder,
href_prefix: "".to_owned(),
href: 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).to_string()
}
fn get_value(&self) -> serde_json::Value {
self.last_value.clone()
}
fn set_value(&mut self, value: serde_json::Value) -> Result<(), &'static str> {
let result = self.validate_value(&value);
if result.is_err() {
return result;
}
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()
}
}