qdb 0.1.10

Library for interacting with QDB
Documentation
use crate::framework::database::Database;
use crate::framework::logger::Logger;
use crate::framework::workers::common::WorkerTrait;
use crate::Result;

use std::cell::RefCell;
use std::rc::Rc;
use std::time::Instant;

pub trait ApplicationTrait {
    fn execute(&mut self);
    fn add_worker(&mut self, worker: Box<dyn WorkerTrait>);
}

type _BoolFlag = Rc<RefCell<bool>>;
pub struct BoolFlag(_BoolFlag);

impl BoolFlag {
    pub fn new() -> Self {
        BoolFlag(Rc::new(RefCell::new(false)))
    }

    pub fn set(&self, value: bool) {
        *self.0.borrow_mut() = value;
    }

    pub fn get(&self) -> bool {
        *self.0.borrow()
    }
}

impl Clone for BoolFlag {
    fn clone(&self) -> Self {
        BoolFlag(self.0.clone())
    }
}

struct _Context {
    pub database: Database,
    pub logger: Logger,
    pub quit: BoolFlag,
}

type ContextRef = Rc<RefCell<_Context>>;
pub struct Context(ContextRef);

impl Context {
    pub fn new(database: Database, logger: Logger) -> Self {
        Context(Rc::new(RefCell::new(_Context {
            database,
            logger,
            quit: BoolFlag::new(),
        })))
    }

    pub fn database(&self) -> Database {
        self.0.borrow().database.clone()
    }

    pub fn logger(&self) -> Logger {
        self.0.borrow().logger.clone()
    }

    pub fn quit(&self) -> BoolFlag {
        self.0.borrow().quit.clone()
    }
}

impl Clone for Context {
    fn clone(&self) -> Self {
        Context(self.0.clone())
    }
}

pub struct Application {
    ctx: Context,
    workers: Vec<Box<dyn WorkerTrait>>,
    loop_interval_ms: u64,
}

impl Application {
    pub fn new(ctx: Context, loop_interval_ms: u64) -> Self {
        Self {
            ctx,
            workers: vec![],
            loop_interval_ms,
        }
    }
}

impl WorkerTrait for Application {
    fn intialize(&mut self, ctx: Context) -> Result<()> {
        let c = format!("{}::{}", std::any::type_name::<Self>(), "initialize");

        ctx.logger().info(
            format!("[{}] Initializing application", c).as_str(),
        );
        for worker in &mut self.workers {
            match worker.intialize(ctx.clone()) {
                Ok(_) => {}
                Err(e) => {
                    ctx.logger().error(&format!(
                        "[{}] Error while initializing worker: {}",
                        c, e
                    ));
                }
            }
        }

        Ok(())
    }

    fn do_work(&mut self, ctx: Context) -> Result<()> {
        let c = format!("{}::{}", std::any::type_name::<Self>(), "do_work");

        ctx.logger().info(
            format!("[{}] Application has started", c).as_str(),
        );

        while {
            let start = Instant::now();

            for i in 0..self.workers.len() {
                let iter_start = Instant::now();

                let worker = &mut self.workers[i];
                match worker.do_work(ctx.clone()) {
                    Ok(_) => {}
                    Err(e) => {
                        ctx.logger().error(&format!(
                            "[{}] Error while executing worker: {}",
                            c, e
                        ));
                    }
                }

                let elapsed_ms = iter_start.elapsed().as_millis();
                ctx.logger().trace(
                    format!("[{}] Worker '{}' took {} ms to complete tick",
                        c, worker.name(), elapsed_ms).as_str());

                match self.process_events() {
                    Ok(_) => {}
                    Err(e) => {
                        ctx.logger().error(&format!(
                            "[{}] Error while processing events: {}",
                            c, e
                        ));
                    }
                }
            }

            if !ctx.quit().get() {
                let loop_time = std::time::Duration::from_millis(self.loop_interval_ms);
                let elapsed_time = start.elapsed();

                if loop_time > elapsed_time {
                    let sleep_time = loop_time - elapsed_time;
                    ctx.logger().trace(&format!(
                        "[{}] Idle for {:?} ms",
                        c, sleep_time.as_millis()
                    ));
                    std::thread::sleep(sleep_time);
                }
            }

            !ctx.quit().get()
        } {}

        Ok(())
    }

    fn deinitialize(&mut self, ctx: Context) -> Result<()> {
        let c = format!("{}::{}", std::any::type_name::<Self>(), "deinitialize");

        ctx.logger().info(
            format!("[{}] Deinitializing application", c).as_str(),
        );

        for worker in &mut self.workers {
            match worker.deinitialize(ctx.clone()) {
                Ok(_) => {}
                Err(e) => {
                    ctx.logger().error(&format!(
                        "[{}] Error while deinitializing worker: {}",
                        c, e
                    ));
                }
            }
        }

        ctx.logger().info(
            format!("[{}] Shutting down now", c).as_str(),
        );
        Ok(())
    }

    fn process_events(&mut self) -> Result<()> {
        let c = format!("{}::{}", std::any::type_name::<Self>(), "process_events");

        for worker in &mut self.workers {
            match worker.process_events() {
                Ok(_) => {}
                Err(e) => {
                    self.ctx.logger().error(&format!(
                        "[{}] Error while processing events: {}",
                        c, e
                    ));
                }
            }
        }

        Ok(())
    }
}

impl ApplicationTrait for Application {
    fn execute(&mut self) {
        self.intialize(self.ctx.clone()).unwrap();
        self.do_work(self.ctx.clone()).unwrap();
        self.deinitialize(self.ctx.clone()).unwrap();
    }

    fn add_worker(&mut self, worker: Box<dyn WorkerTrait>) {
        self.workers.push(worker);
    }
}