boxcap 0.1.0

Common project structure for creating rust projects
Documentation
//! The services module represents the shared code between all services
//! Children elements should implement "Service"
//! Children modules under boxcapp::services house that services implmentation. Types are aliased at the service level,
//! this means the usage for a service is as follows
//! ```no_run
//! let db = services::Database::new()
//! ```
//! Instead of
//! ```no_run
//! let db = services::database::Database::new()
//! ```
//!

use super::{Message, Messenger};
use std::error::Error;
use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender};
use std::thread::sleep;
use std::time::Duration;

// Services (Alias the types so they are available in the outer scope)
pub mod database;
/// Alias the database struct from the database module so you dont have to nest it to access it
pub type Database = database::Database;

pub mod www;

/// Services are the functional blocks that bridge the data and application layers
pub trait Service: Send {
    /// Run the particular service
    fn run(&self) -> Result<(), Box<dyn Error>>;

    /// sends a message to whoever is in the "to" field of the message
    fn send(&self, msg: Message) -> Result<(), Box<dyn Error>>;

    /// The dispactcher will keep track of everyones ID
    fn identify(&self) -> String;

    /// Sets the comm pair for the service. Tx will send to the messenger, rx will receive from the messenger
    fn set_comm_pair(&mut self, tx: Sender<Message>, rx: Receiver<Message>) -> ();
}

/// Accepts both a service and a messenger, and assigns them a dual channel tx, rx pair so the service can send messages
/// between threads
pub fn map_comm_pairs(service: &mut Box<dyn Service>, messenger: &mut Messenger) {
    let (messenger_tx, service_rx) = mpsc::channel::<Message>();
    let (service_tx, messenger_rx) = mpsc::channel::<Message>();

    service.set_comm_pair(service_tx, service_rx);
    messenger.set_comm_pair(service.identify(), (messenger_tx, messenger_rx));
}

/// Test service is helpful to test and build various services

pub struct TestService {
    name: String,
    rx: Option<Receiver<Message>>,
    tx: Option<Sender<Message>>,
}

impl TestService {
    /// Creates a new instance of a test service
    pub fn new(name: &str) -> Self {
        TestService {
            name: name.to_string(),
            rx: None,
            tx: None,
        }
    }
}

impl Service for TestService {
    /// Run the particular service
    fn run(&self) -> Result<(), Box<dyn Error>> {
        let msg = Message::new(&self.name, "test", "This is the message content");
        self.send(msg)
    }

    /// sends a message to
    fn send(&self, msg: Message) -> Result<(), Box<dyn Error>> {
        if let Some(tx) = &self.tx {
            tx.send(msg)?
        }
        Ok(())
    }

    /// The dispactcher will keep track of everyones ID
    fn identify(&self) -> String {
        return self.name.clone();
    }

    ///
    fn set_comm_pair(&mut self, tx: Sender<Message>, rx: Receiver<Message>) -> () {
        self.tx = Some(tx);
        self.rx = Some(rx);
    }
}

/// The testing suite for the library can be seen here
#[cfg(test)]
mod tests {

    use std::{fs, path::Path};

    fn clean() -> Result<(), Box<dyn std::error::Error>> {
        // Define the path to the logs directory
        let log_dir_path = Path::new("logs/tests");

        // Check if the directory exists
        if log_dir_path.exists() {
            // Remove the directory and its contents
            fs::remove_dir_all(log_dir_path)?;
            println!("Successfully removed {}", log_dir_path.display());
        } else {
            println!("Directory {} does not exist.", log_dir_path.display());
        }

        Ok(())
    }

    #[tokio::test]
    async fn test_db() {}
}