new-home-application 0.1.3

New Home iot application framework. Meant to build application for the New Home Core
Documentation
use std::io::{Error, ErrorKind};

use crate::communication::client_argument_parser::{ClientArgumentParser, ClientArgumentParserImpl};
use crate::communication::request::RequestData;
use crate::method::method_manager::MethodManager;
use crate::method::method_structure::MethodResult;
use crate::method::response_formatter::JsonResponseFormatter;
use crate::net::tcp_reader::TcpReader;
use crate::net::tcp_writer::TcpWriter;

/// The `ClientHandler` implementation struct
/// Uses the `TcpReader`, `TcpWriter` and `MethodManager` to handle and answering requests
pub struct ClientHandlerImpl<'a> {
    /// The applications `MethodManager` that contains all registered methods with their callbacks
    /// Is received in the constructor directly
    method_manager: &'a Box<dyn MethodManager>,

    /// The `TcpReader` which is connected to the connected `TcpStream` (aka. TCP clients connection stream)
    /// Is received in the constructor directly
    reader: Box<dyn TcpReader>,

    /// The `TcpWriter` which is connected to the connected `TcpStream` (aka. TCP clients connection stream)
    /// Is received in the constructor directly
    writer: Box<dyn TcpWriter>,
}

/// The interface for the client handler
/// Interacts with the connected client
/// Receives the requests, handles it and sends the response back
/// Returns errors in the form of the `std::io::Error` struct
pub trait ClientHandler {
    /// Handles the client connection
    /// Reads the request in JSON format via the given `TcpReader`
    /// The responsible method callback will be requested from the `MethodManager`
    /// Writs the result in JSON format back via the given `TcpWriter`
    /// Errors that occur are send back in JSON format as well with the given `TcpWriter`
    fn handle(&mut self) -> Result<(), Error>;
}

impl ClientHandler for ClientHandlerImpl<'_> {
    fn handle(&mut self) -> Result<(), Error> {
        let result = self.get_request();

        if result.is_err() {
            self.send_error(String::from("There was an error in your JSON string"))?;

            return Ok(());
        }

        let request = result.unwrap();
        let response = self.handle_method(request)?;
        self.send_response(response)?;

        Ok(())
    }
}

impl<'a> ClientHandlerImpl<'a> {
    pub fn new(writer: Box<dyn TcpWriter>, reader: Box<dyn TcpReader>, method_manager: &'a Box<dyn MethodManager>) -> Self {
        Self {
            reader,
            writer,
            method_manager,
        }
    }

    fn get_request(&mut self) -> Result<RequestData, Error> {
        let buf = self.reader.read_line()?;
        let result = serde_json::from_str(&buf);

        match result {
            Err(_e) => Err(Error::new(ErrorKind::Other, "Could not parse JSON")),
            Ok(request_data) => Ok(request_data),
        }
    }

    fn send_error(&mut self, message: String) -> Result<(), Error> {
        self.send_response(JsonResponseFormatter::error(&message))?;

        Ok(())
    }

    fn handle_method(&mut self, data: RequestData) -> Result<MethodResult, Error> {
        let callback_option = self.method_manager.get_method_callback(&data.method_name);

        if callback_option.is_none() {
            return Ok(JsonResponseFormatter::error(&String::from("Method not found!")));
        }

        let callback = callback_option.unwrap();
        let method_definition = self.method_manager.get_method(&data.method_name).unwrap();
        let parser = ClientArgumentParserImpl {};
        let method_arguments = parser.parse(method_definition.arguments.clone(), &data.arguments);

        if method_arguments.is_err() {
            return Ok(JsonResponseFormatter::error(
                &method_arguments.as_ref().err().unwrap().to_string()
            ));
        }

        Ok(callback.call(method_arguments.unwrap()).to_owned())
    }

    fn send_response(&mut self, result: MethodResult) -> Result<(), Error> {
        let result = serde_json::to_string(&result)?;
        self.writer.write_line(&result)?;

        Ok(())
    }
}