use serde_json;
use serde_json::json;
use std::any::Any;
use std::collections::HashMap;
use std::marker::{Send, Sync};
use std::sync::{Arc, RwLock};
use std::vec::Drain;
use valico::json_schema;
use super::action::Action;
use super::event::Event;
use super::property::Property;
pub trait Thing: Send + Sync {
fn as_thing_description(&self) -> serde_json::Map<String, serde_json::Value>;
fn as_any(&self) -> &dyn Any;
fn as_mut_any(&mut self) -> &mut dyn Any;
fn get_href(&self) -> String;
fn get_href_prefix(&self) -> String;
fn get_ui_href(&self) -> Option<String>;
fn set_href_prefix(&mut self, prefix: String);
fn set_ui_href(&mut self, href: String);
fn get_id(&self) -> String;
fn get_title(&self) -> String;
fn get_context(&self) -> serde_json::Value;
fn get_type(&self) -> Vec<String>;
fn get_description(&self) -> String;
fn get_property_descriptions(&self) -> serde_json::Map<String, serde_json::Value>;
fn get_action_descriptions(&self, action_name: Option<String>) -> serde_json::Value;
fn get_event_descriptions(&self, event_name: Option<String>) -> serde_json::Value;
fn add_property(&mut self, property: Box<dyn Property>);
fn remove_property(&mut self, property_name: &str);
fn find_property(&mut self, property_name: &str) -> Option<&mut Box<dyn Property>>;
fn get_property(&self, property_name: &str) -> Option<serde_json::Value>;
fn get_properties(&self) -> serde_json::Map<String, serde_json::Value>;
fn has_property(&self, property_name: &str) -> bool;
fn set_property(
&mut self,
property_name: String,
value: serde_json::Value,
) -> Result<(), &'static str> {
let property = self
.find_property(&property_name)
.ok_or("Property not found")?;
property.set_value(value.clone())?;
self.property_notify(property_name, value);
Ok(())
}
fn get_action(
&self,
action_name: String,
action_id: String,
) -> Option<Arc<RwLock<Box<dyn Action>>>>;
fn add_event(&mut self, event: Box<dyn Event>);
fn add_available_event(
&mut self,
name: String,
metadata: serde_json::Map<String, serde_json::Value>,
);
fn add_action(
&mut self,
action: Arc<RwLock<Box<dyn Action>>>,
input: Option<&serde_json::Value>,
) -> Result<(), &str>;
fn remove_action(&mut self, action_name: String, action_id: String) -> bool;
fn add_available_action(
&mut self,
name: String,
metadata: serde_json::Map<String, serde_json::Value>,
);
fn add_subscriber(&mut self, ws_id: String);
fn remove_subscriber(&mut self, ws_id: String);
fn add_event_subscriber(&mut self, name: String, ws_id: String);
fn remove_event_subscriber(&mut self, name: String, ws_id: String);
fn property_notify(&mut self, name: String, value: serde_json::Value);
fn action_notify(&mut self, action: serde_json::Map<String, serde_json::Value>);
fn event_notify(&mut self, name: String, event: serde_json::Map<String, serde_json::Value>);
fn start_action(&mut self, name: String, id: String);
fn cancel_action(&mut self, name: String, id: String);
fn finish_action(&mut self, name: String, id: String);
fn drain_queue(&mut self, ws_id: String) -> Vec<Drain<String>>;
}
#[derive(Clone)]
pub struct ThingContext {
plain: Vec<String>,
namespaced: HashMap<String, String>,
}
impl ThingContext {
pub fn new(base: &str) -> Self {
ThingContext {
plain: vec![base.to_owned()],
namespaced: HashMap::new(),
}
}
}
impl<A, B> std::iter::FromIterator<(Option<A>, B)> for ThingContext
where
A: AsRef<str>,
B: AsRef<str>,
{
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = (Option<A>, B)>,
{
let mut plain = Vec::new();
let mut namespaced = HashMap::new();
for (maybe_k, val) in iter {
let val = val.as_ref().to_owned();
if let Some(k) = maybe_k {
namespaced.insert(k.as_ref().to_owned(), val);
} else {
plain.push(val);
}
}
ThingContext { plain, namespaced }
}
}
impl Default for ThingContext {
fn default() -> Self {
ThingContext::new("https://webthings.io/schemas")
}
}
#[derive(Default)]
pub struct BaseThing {
id: String,
context: ThingContext,
type_: Vec<String>,
title: String,
description: String,
properties: HashMap<String, Box<dyn Property>>,
available_actions: HashMap<String, AvailableAction>,
available_events: HashMap<String, AvailableEvent>,
actions: HashMap<String, Vec<Arc<RwLock<Box<dyn Action>>>>>,
events: Vec<Box<dyn Event>>,
subscribers: HashMap<String, Vec<String>>,
href_prefix: String,
ui_href: Option<String>,
}
impl BaseThing {
pub fn new(
id: String,
title: String,
type_: Option<Vec<String>>,
description: Option<String>,
) -> Self {
Self {
id,
type_: type_.unwrap_or_else(Vec::new),
title,
description: description.unwrap_or_else(|| "".to_string()),
..Default::default()
}
}
pub fn with_context(mut self, context: ThingContext) -> Self {
self.context = context;
self
}
}
impl Thing for BaseThing {
fn as_thing_description(&self) -> serde_json::Map<String, serde_json::Value> {
let mut description = serde_json::Map::new();
description.insert("id".to_owned(), json!(self.get_id()));
description.insert("title".to_owned(), json!(self.get_title()));
description.insert("@context".to_owned(), self.get_context());
description.insert("@type".to_owned(), json!(self.get_type()));
description.insert(
"properties".to_owned(),
json!(self.get_property_descriptions()),
);
let mut links: Vec<serde_json::Map<String, serde_json::Value>> = Vec::new();
let mut properties_link = serde_json::Map::new();
properties_link.insert("rel".to_owned(), json!("properties"));
properties_link.insert(
"href".to_owned(),
json!(format!("{}/properties", self.get_href_prefix())),
);
links.push(properties_link);
let mut actions_link = serde_json::Map::new();
actions_link.insert("rel".to_owned(), json!("actions"));
actions_link.insert(
"href".to_owned(),
json!(format!("{}/actions", self.get_href_prefix())),
);
links.push(actions_link);
let mut events_link = serde_json::Map::new();
events_link.insert("rel".to_owned(), json!("events"));
events_link.insert(
"href".to_owned(),
json!(format!("{}/events", self.get_href_prefix())),
);
links.push(events_link);
if let Some(ui_href) = self.get_ui_href() {
let mut ui_link = serde_json::Map::new();
ui_link.insert("rel".to_owned(), json!("alternate"));
ui_link.insert("mediaType".to_owned(), json!("text/html"));
ui_link.insert("href".to_owned(), json!(ui_href));
links.push(ui_link);
}
description.insert("links".to_owned(), json!(links));
let mut actions = serde_json::Map::new();
for (name, action) in self.available_actions.iter() {
let mut metadata = action.get_metadata().clone();
metadata.insert(
"links".to_string(),
json!([
{
"rel": "action",
"href": format!("{}/actions/{}", self.get_href_prefix(), name),
},
]),
);
actions.insert(name.to_string(), json!(metadata));
}
description.insert("actions".to_owned(), json!(actions));
let mut events = serde_json::Map::new();
for (name, event) in self.available_events.iter() {
let mut metadata = event.get_metadata().clone();
metadata.insert(
"links".to_string(),
json!([
{
"rel": "event",
"href": format!("{}/events/{}", self.get_href_prefix(), name),
},
]),
);
events.insert(name.to_string(), json!(metadata));
}
description.insert("events".to_owned(), json!(events));
if !self.description.is_empty() {
description.insert("description".to_owned(), json!(self.description));
}
description
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_mut_any(&mut self) -> &mut dyn Any {
self
}
fn get_href(&self) -> String {
if self.href_prefix.is_empty() {
"/".to_owned()
} else {
self.href_prefix.clone()
}
}
fn get_href_prefix(&self) -> String {
self.href_prefix.clone()
}
fn get_ui_href(&self) -> Option<String> {
self.ui_href.clone()
}
fn set_href_prefix(&mut self, prefix: String) {
self.href_prefix = prefix.clone();
for property in self.properties.values_mut() {
property.set_href_prefix(prefix.clone());
}
for actions in self.actions.values_mut() {
for action in actions {
action.write().unwrap().set_href_prefix(prefix.clone());
}
}
}
fn set_ui_href(&mut self, href: String) {
self.ui_href = Some(href);
}
fn get_id(&self) -> String {
self.id.clone()
}
fn get_title(&self) -> String {
self.title.clone()
}
fn get_context(&self) -> serde_json::Value {
if self.context.namespaced.is_empty() {
if self.context.plain.len() == 1 {
json!(self.context.plain[0])
} else {
json!(self.context.plain)
}
} else {
let mut values = json!(self.context.plain);
if let Some(v) = values.as_array_mut() {
v.push(json!(self.context.namespaced))
}
values
}
}
fn get_type(&self) -> Vec<String> {
self.type_.clone()
}
fn get_description(&self) -> String {
self.description.clone()
}
fn get_property_descriptions(&self) -> serde_json::Map<String, serde_json::Value> {
let mut descriptions = serde_json::Map::new();
for (name, property) in self.properties.iter() {
descriptions.insert(name.to_string(), json!(property.as_property_description()));
}
descriptions
}
fn get_action_descriptions(&self, action_name: Option<String>) -> serde_json::Value {
let mut descriptions = Vec::new();
match action_name {
Some(action_name) => {
if let Some(actions) = self.actions.get(&action_name) {
for action in actions {
descriptions.push(action.read().unwrap().as_action_description());
}
}
}
None => {
for action in self.actions.values().flatten() {
descriptions.push(action.read().unwrap().as_action_description());
}
}
}
json!(descriptions)
}
fn get_event_descriptions(&self, event_name: Option<String>) -> serde_json::Value {
let mut descriptions = Vec::new();
match event_name {
Some(event_name) => {
for event in &self.events {
if event.get_name() == event_name {
descriptions.push(event.as_event_description());
}
}
}
None => {
for event in &self.events {
descriptions.push(event.as_event_description());
}
}
}
json!(descriptions)
}
fn add_property(&mut self, mut property: Box<dyn Property>) {
property.set_href_prefix(self.get_href_prefix());
self.properties.insert(property.get_name(), property);
}
fn remove_property(&mut self, property_name: &str) {
self.properties.remove(property_name);
}
fn find_property(&mut self, property_name: &str) -> Option<&mut Box<dyn Property>> {
self.properties.get_mut(property_name)
}
fn get_property(&self, property_name: &str) -> Option<serde_json::Value> {
self.properties.get(property_name).map(|p| p.get_value())
}
fn get_properties(&self) -> serde_json::Map<String, serde_json::Value> {
let mut properties = serde_json::Map::new();
for (name, property) in self.properties.iter() {
properties.insert(name.to_string(), json!(property.get_value()));
}
properties
}
fn has_property(&self, property_name: &str) -> bool {
self.properties.contains_key(property_name)
}
fn get_action(
&self,
action_name: String,
action_id: String,
) -> Option<Arc<RwLock<Box<dyn Action>>>> {
match self.actions.get(&action_name) {
Some(entry) => {
for action in entry {
if action.read().unwrap().get_id() == action_id {
return Some(action.clone());
}
}
None
}
None => None,
}
}
fn add_event(&mut self, event: Box<dyn Event>) {
self.event_notify(event.get_name(), event.as_event_description());
self.events.push(event);
}
fn add_available_event(
&mut self,
name: String,
metadata: serde_json::Map<String, serde_json::Value>,
) {
let event = AvailableEvent::new(metadata);
self.available_events.insert(name, event);
}
fn add_action(
&mut self,
action: Arc<RwLock<Box<dyn Action>>>,
input: Option<&serde_json::Value>,
) -> Result<(), &str> {
let action_name = action.read().unwrap().get_name();
if let Some(action_type) = self.available_actions.get(&action_name) {
if !action_type.validate_action_input(input) {
return Err("Action input invalid");
}
} else {
return Err("Action type not found");
}
action
.write()
.unwrap()
.set_href_prefix(self.get_href_prefix());
self.action_notify(action.read().unwrap().as_action_description());
self.actions.get_mut(&action_name).unwrap().push(action);
Ok(())
}
fn remove_action(&mut self, action_name: String, action_id: String) -> bool {
let action = self.get_action(action_name.clone(), action_id.clone());
match action {
Some(action) => {
action.write().unwrap().cancel();
let actions = self.actions.get_mut(&action_name).unwrap();
actions.retain(|a| a.read().unwrap().get_id() != action_id);
true
}
None => false,
}
}
fn add_available_action(
&mut self,
name: String,
metadata: serde_json::Map<String, serde_json::Value>,
) {
let action = AvailableAction::new(metadata);
self.available_actions.insert(name.clone(), action);
self.actions.insert(name, Vec::new());
}
fn add_subscriber(&mut self, ws_id: String) {
self.subscribers.insert(ws_id, Vec::new());
}
fn remove_subscriber(&mut self, ws_id: String) {
self.subscribers.remove(&ws_id);
for event in self.available_events.values_mut() {
event.remove_subscriber(ws_id.clone());
}
}
fn add_event_subscriber(&mut self, name: String, ws_id: String) {
if let Some(event) = self.available_events.get_mut(&name) {
event.add_subscriber(ws_id);
}
}
fn remove_event_subscriber(&mut self, name: String, ws_id: String) {
if let Some(event) = self.available_events.get_mut(&name) {
event.remove_subscriber(ws_id);
}
}
fn property_notify(&mut self, name: String, value: serde_json::Value) {
let message = json!({
"messageType": "propertyStatus",
"data": {
name: value
}
})
.to_string();
self.subscribers
.values_mut()
.for_each(|queue| queue.push(message.clone()));
}
fn action_notify(&mut self, action: serde_json::Map<String, serde_json::Value>) {
let message = json!({
"messageType": "actionStatus",
"data": action
})
.to_string();
self.subscribers
.values_mut()
.for_each(|queue| queue.push(message.clone()));
}
fn event_notify(&mut self, name: String, event: serde_json::Map<String, serde_json::Value>) {
if !self.available_events.contains_key(&name) {
return;
}
let message = json!({
"messageType": "event",
"data": event,
})
.to_string();
self.available_events
.get_mut(&name)
.unwrap()
.get_subscribers()
.values_mut()
.for_each(|queue| queue.push(message.clone()));
}
fn start_action(&mut self, name: String, id: String) {
if let Some(action) = self.get_action(name, id) {
let mut a = action.write().unwrap();
a.start();
self.action_notify(a.as_action_description());
a.perform_action();
}
}
fn cancel_action(&mut self, name: String, id: String) {
if let Some(action) = self.get_action(name, id) {
let mut a = action.write().unwrap();
a.cancel();
}
}
fn finish_action(&mut self, name: String, id: String) {
if let Some(action) = self.get_action(name, id) {
let mut a = action.write().unwrap();
a.finish();
self.action_notify(a.as_action_description());
}
}
fn drain_queue(&mut self, ws_id: String) -> Vec<Drain<String>> {
let mut drains: Vec<Drain<String>> = Vec::new();
if let Some(v) = self.subscribers.get_mut(&ws_id) {
drains.push(v.drain(..));
}
self.available_events.values_mut().for_each(|evt| {
if let Some(v) = evt.get_subscribers().get_mut(&ws_id) {
drains.push(v.drain(..));
}
});
drains
}
}
struct AvailableAction {
metadata: serde_json::Map<String, serde_json::Value>,
}
impl AvailableAction {
fn new(metadata: serde_json::Map<String, serde_json::Value>) -> AvailableAction {
AvailableAction { metadata }
}
fn get_metadata(&self) -> &serde_json::Map<String, serde_json::Value> {
&self.metadata
}
fn validate_action_input(&self, input: Option<&serde_json::Value>) -> bool {
let mut scope = json_schema::Scope::new();
let validator = if let Some(input) = self.metadata.get("input") {
let mut schema = input.as_object().unwrap().clone();
if let Some(properties) = schema.get_mut("properties") {
let properties = properties.as_object_mut().unwrap();
for value in properties.values_mut() {
let value = value.as_object_mut().unwrap();
value.remove("@type");
value.remove("unit");
value.remove("title");
}
}
match scope.compile_and_return(json!(schema), true) {
Ok(s) => Some(s),
Err(_) => None,
}
} else {
None
};
match validator {
Some(ref v) => match input {
Some(i) => v.validate(i).is_valid(),
None => v.validate(&serde_json::Value::Null).is_valid(),
},
None => true,
}
}
}
struct AvailableEvent {
metadata: serde_json::Map<String, serde_json::Value>,
subscribers: HashMap<String, Vec<String>>,
}
impl AvailableEvent {
fn new(metadata: serde_json::Map<String, serde_json::Value>) -> AvailableEvent {
AvailableEvent {
metadata,
subscribers: HashMap::new(),
}
}
fn get_metadata(&self) -> &serde_json::Map<String, serde_json::Value> {
&self.metadata
}
fn add_subscriber(&mut self, ws_id: String) {
self.subscribers.insert(ws_id, Vec::new());
}
fn remove_subscriber(&mut self, ws_id: String) {
self.subscribers.remove(&ws_id);
}
fn get_subscribers(&mut self) -> &mut HashMap<String, Vec<String>> {
&mut self.subscribers
}
}