woopsa 0.1.1

A pure Rust library for Woopsa.
Documentation
use serde::{Deserialize, Serialize};

use crate::protocol::constant::*;
use crate::protocol::container::WoopsaContainer;
use crate::protocol::element::WoopsaElement;
use crate::protocol::method::WoopsaMethod;
use crate::protocol::property::WoopsaProperty;

use std::collections::HashMap;
use std::fmt;

pub trait Object {
    fn type_of(&self) -> &'static str;
}

#[derive(Serialize, Deserialize)]
pub struct WoopsaObject {
    pub container: WoopsaContainer,
    pub properties: HashMap<String, WoopsaProperty>,
    pub methods: HashMap<String, WoopsaMethod>,
    is_root_object: bool,
}

impl WoopsaObject {
    pub fn new(element_name: String) -> WoopsaObject {
        WoopsaObject {
            container: WoopsaContainer {
                element: WoopsaElement { name: element_name },
                items: HashMap::new(),
            },
            properties: HashMap::new(),
            methods: HashMap::new(),
            is_root_object: false,
        }
    }

    pub fn root() -> WoopsaObject {
        WoopsaObject {
            container: WoopsaContainer {
                element: WoopsaElement {
                    name: String::from(WOOPSA_ROOT_ELEMENT_NAME),
                },
                items: HashMap::new(),
            },
            properties: HashMap::new(),
            methods: HashMap::new(),
            is_root_object: true,
        }
    }

    pub fn is_root(&self) -> bool {
        self.is_root_object
    }

    pub fn is_not_root(&self) -> bool {
        !self.is_root_object
    }

    pub fn name(&self) -> String {
        return self.container.element.name.clone();
    }

    pub fn set_name(&mut self, name: String) {
        self.container.element.name = name;
    }

    pub fn register_container(&mut self, container: WoopsaContainer) {
        self.container = container;
    }

    pub fn insert_item(&mut self, item: WoopsaObject) {
        self.container.insert_item(item);
    }

    pub fn remove_item(&mut self, item: WoopsaObject) {
        self.container.remove_item(item);
    }

    pub fn add_property(&mut self, property: WoopsaProperty) {
        self.properties
            .insert(property.element.name.clone(), property);
    }

    pub fn remove_property(&mut self, property: WoopsaProperty) {
        self.properties.remove(&(property.element.name));
    }

    pub fn add_method(&mut self, method: WoopsaMethod) {
        self.methods.insert(method.element.name.clone(), method);
    }

    pub fn remove_method(&mut self, method: WoopsaMethod) {
        self.methods.remove(&(method.element.name));
    }

    pub fn find_item_by_name(&self, name: String) -> &WoopsaObject {
        return self.container.items.get(&name).unwrap();
    }

    pub fn find_property_by_name(&self, name: String) -> &WoopsaProperty {
        return self.properties.get(&name).unwrap();
    }

    pub fn find_method_by_name(&self, name: String) -> &WoopsaMethod {
        return self.methods.get(&name).unwrap();
    }

    pub fn clear(&mut self) {
        self.container.clear();
        self.properties.clear();
        self.methods.clear();
    }

    pub fn get_path(&self) -> String {
        let mut path = String::new();
        if self.is_not_root() {
            path.push(WOOPSA_PATH_SEPARATOR);
            path.push_str(self.name().as_str());
        } else {
            path.push(WOOPSA_ROOT_PATH);
        }
        path
    }

    pub fn get_item_path(&self, item_name: String) -> String {
        let mut path = String::new();
        if self.is_not_root() {
            path.push(WOOPSA_PATH_SEPARATOR);
            path.push_str(self.name().as_str());
            path.push(WOOPSA_PATH_SEPARATOR);
            path.push_str(self.find_item_by_name(item_name).name().as_str());
        } else {
            if WOOPSA_ROOT_PATH.eq_ignore_ascii_case(&WOOPSA_PATH_SEPARATOR) {
                path.push(WOOPSA_ROOT_PATH);
            } else {
                path.push(WOOPSA_ROOT_PATH);
                path.push(WOOPSA_PATH_SEPARATOR);
            }
            path.push_str(self.find_item_by_name(item_name).name().as_str());
        }
        path
    }

    pub fn get_item_by_path(&self, item_path: String) -> &WoopsaObject {
        let mut name = String::new();

        if item_path.contains(WOOPSA_PATH_SEPARATOR) {
            let mut names: Vec<&str> = item_path.split(WOOPSA_PATH_SEPARATOR).collect();
            names.reverse();
            let first_path = names.pop().unwrap();
            if first_path.is_empty() {
                name.push(WOOPSA_PATH_SEPARATOR);
                name.push_str(names.pop().unwrap());
            } else {
                name.push_str(first_path);
            }
        } else {
            name.push_str(item_path.as_str());
        }
        
        let next_item_path = item_path.replace(name.as_str(), "");
        if name.contains(WOOPSA_PATH_SEPARATOR) || next_item_path.contains(WOOPSA_PATH_SEPARATOR) {
            let next_group_item = self.find_item_by_name(name.replace(WOOPSA_PATH_SEPARATOR, ""));
            if next_item_path.is_empty() {
                next_group_item
            } else {
                next_group_item.get_item_by_path(next_item_path)
            }
        } else {
            self.find_item_by_name(name)
        }
    }
}

impl Object for WoopsaObject {
    fn type_of(&self) -> &'static str {
        "WoopsaObject"
    }
}

impl fmt::Display for WoopsaObject {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "(object named {} with items count {}, properties count {}, and methods count {})",
            self.name(),
            self.container.items.len(),
            self.properties.len(),
            self.methods.len()
        )
    }
}

#[test]
fn it_should_get_woopsaobject_root_path() {
    let root = WoopsaObject::root();
    assert_eq!(root.get_path(), "/");
}

#[test]
fn it_should_get_woopsaobject_path() {
    let objects = WoopsaObject::new(String::from("Objects"));
    assert_eq!(objects.get_path(), "/Objects");
}

#[test]
fn it_should_get_woopsaobject_path_in_hierarchy() {
    let mut root = WoopsaObject::root();
    let mut objects = WoopsaObject::new(String::from("Objects"));
    objects.insert_item(WoopsaObject::new(String::from("WeatherStation")));
    root.insert_item(objects);
    let objects = root.find_item_by_name(String::from("Objects"));
    let weather_station = objects.find_item_by_name(String::from("WeatherStation"));
    assert_eq!(weather_station.get_path(), "/WeatherStation");
}

#[test]
fn it_should_get_woopsaobject_item_path_in_hierarchy() {
    let mut root = WoopsaObject::root();
    let mut objects = WoopsaObject::new(String::from("Objects"));
    objects.insert_item(WoopsaObject::new(String::from("WeatherStation")));
    root.insert_item(objects);
    let objects = root.find_item_by_name(String::from("Objects"));
    assert_eq!(
        objects.get_item_path(String::from("WeatherStation")),
        "/Objects/WeatherStation"
    );
}

#[test]
fn it_should_get_woopsaobject_item_path_from_root() {
    let mut root = WoopsaObject::root();
    let mut objects = WoopsaObject::new(String::from("Objects"));
    objects.insert_item(WoopsaObject::new(String::from("WeatherStation")));
    root.insert_item(objects);
    assert_eq!(root.get_item_path(String::from("Objects")), "/Objects");
}

#[test]
fn it_should_get_woopsaobject_item_by_root_path_from_hierarchy() {
    let mut root = WoopsaObject::root();
    let mut objects = WoopsaObject::new(String::from("Objects"));
    let mut weatherstation = WoopsaObject::new(String::from("WeatherStation"));
    weatherstation.insert_item(WoopsaObject::new(String::from("Thermostat")));
    objects.insert_item(weatherstation);
    root.insert_item(objects);
    assert_eq!(
        root.get_item_by_path(String::from("/Objects/WeatherStation/Thermostat"))
            .name(),
        String::from("Thermostat")
    );
}

#[test]
fn it_should_get_woopsaobject_item_by_path_from_hierarchy() {
    let mut root = WoopsaObject::root();
    let mut objects = WoopsaObject::new(String::from("Objects"));
    let mut weatherstation = WoopsaObject::new(String::from("WeatherStation"));
    weatherstation.insert_item(WoopsaObject::new(String::from("Thermostat")));
    objects.insert_item(weatherstation);
    root.insert_item(objects);

    let objects = root.find_item_by_name(String::from("Objects"));
    assert_eq!(
        objects
            .get_item_by_path(String::from("WeatherStation/Thermostat"))
            .name(),
        String::from("Thermostat")
    );
}