new-home-application 0.1.3

New Home iot application framework. Meant to build application for the New Home Core
Documentation
use std::sync::Arc;
use std::thread;

use crate::application::app_error::AppError;
use crate::application::application::{ApplicationConfig, ApplicationInfo};
use crate::application::commands::help_command::HelpCommand;
use crate::application::commands::info_command::InfoCommand;
use crate::communication::communication_manager::{CommunicationManager, CommunicationManagerImpl};
use crate::method::method_manager::{MethodManager, MethodManagerImpl};
use crate::net::tcp_server::{TcpServer, TcpServerImpl};

/// This trait has to be implemented by the main application
/// It is used to get all the required data for the `Application` to be `boot`ed and `run`
pub trait ApplicationFramework {
    /// Returns the port on which the application should run
    fn get_application_port(&self) -> i16;

    /// Contains the ip on which the application should be run
    fn get_application_ip(&self) -> String;

    /// Contains the general application info
    fn get_application_info(&self) -> ApplicationInfo;

    /// Used to register your methods in the method_manager
    /// Methods can only be registered here, once
    fn setup_method_manager(&self, method_manager: &mut Box<dyn MethodManager>);
}

/// The application structure contains all the required objects and infos for the application's
/// `boot` and `run` process
/// Once an implemented `ApplicationFramework` is given to it, the methods `boot` and `run` will
/// create the `ApplicationConfig` and the `CommunicationManager` with the help of the provided data
/// by the `ApplicationFramework` to handle requests by connecting clients
pub struct Application {
    /// The application specific `ApplicationFramework` instance used to initialize the application
    pub application_framework: Box<dyn ApplicationFramework>,

    /// When `boot`ed, contains the fully built `ApplicationConfig` struct
    pub application_config: Option<ApplicationConfig>,

    /// When `boot`ed, contains a filled `CommunicationManager`
    /// While created the required dependencies will be build too
    pub communication_manager: Option<Box<dyn CommunicationManager + Send + Sync>>,

    /// A flag to determine if the `Application` was already booted or not
    booted: bool,
}

/// Used to boot the `Application`
/// Should only be used once
pub trait BootApplication {
    /// Prepares the `Application` to be `run`
    /// The boot method should only be callable once, a second call should result in an error
    fn boot(&mut self) -> Result<(), AppError>;

    /// The `run` method will make use of the objects created in the `boot` process
    /// It can be run in multiple threads to handle multiple clients at the same time
    fn run(&self) -> Result<(), AppError>;

    /// Calls the `run` method in multiple threads.
    /// Count of threads can be given with the `thread_count` argument
    fn multi_run(self, thread_count: i32) -> Result<(), AppError>;
}

impl BootApplication for Application {
    fn boot(&mut self) -> Result<(), AppError> {
        if self.booted {
            return Err(AppError::new(None, String::from("Application is already booted!")));
        }

        if self.application_config.is_none() {
            self.build_application_config();
        }

        if self.communication_manager.is_none() {
            self.build_communication_manager()?;
        }

        self.communication_manager.as_mut().unwrap().run();

        self.booted = true;

        Ok(())
    }

    fn run(&self) -> Result<(), AppError> {
        let communication_manager = self.communication_manager.as_ref().unwrap();

        loop {
            let handle_result = communication_manager.handle_client();

            if handle_result.is_err() {
                let error = handle_result.err().unwrap();
                let message = error.to_string().clone();

                return Err(AppError::new(Some(Box::new(error)), message));
            }
        }
    }

    fn multi_run(self, thread_count: i32) -> Result<(), AppError> {
        let mut threads = vec![];
        let arc_self = Arc::new(self);

        for _ in 0..thread_count {
            let thread_app = arc_self.clone();

            threads.push(thread::spawn(move || {
                let run = thread_app.run();

                if run.is_err() {
                    println!("Thread errored: {:?}", run.err().unwrap());

                    return;
                }
            }));
        }

        for thread in threads {
            if thread.join().is_err() {
                println!("Could not join thread")
            }
        }

        Ok(())
    }
}

impl Application {
    pub fn new(application_framework: Box<dyn ApplicationFramework>) -> Self {
        Self {
            application_framework,
            booted: false,
            application_config: None,
            communication_manager: None,
        }
    }

    fn build_application_config(&mut self) {
        self.application_config = Some(ApplicationConfig {
            app_info: self.application_framework.get_application_info(),
            ip: self.application_framework.get_application_ip(),
            port: self.application_framework.get_application_port(),
        });
    }

    fn build_communication_manager(&mut self) -> Result<(), AppError> {
        self.communication_manager = Some(Box::new(CommunicationManagerImpl::new(
            self.build_server()?,
            self.build_method_manager()?,
        )));

        Ok(())
    }

    fn build_server(&mut self) -> Result<Box<dyn TcpServer>, AppError> {
        let config = &self.application_config.as_ref().unwrap();

        let server: Box<dyn TcpServer> = Box::new(TcpServerImpl::new(
            config.port,
            Some(config.ip.clone()),
        ));

        Ok(server)
    }

    fn build_method_manager(&mut self) -> Result<Box<dyn MethodManager>, AppError> {
        let mut method_manager: Box<dyn MethodManager> = Box::new(MethodManagerImpl::new());

        InfoCommand::self_register(&mut method_manager, self.application_config.clone().unwrap());

        self.application_framework.as_ref().setup_method_manager(&mut method_manager);

        HelpCommand::self_register(&mut method_manager);

        Ok(method_manager)
    }
}

unsafe impl Send for Application {}

unsafe impl Sync for Application {}