rsbx 2.0.0

Enhanced implementation of SeqBox in Rust
Documentation
use file_reader::{FileReader,
                  FileReaderParam};
use file_writer::{FileWriter,
                  FileWriterParam};
use general_error::Error;
use std::fmt;

use std::time::Duration;

use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;

use std::sync::Arc;
use std::sync::Mutex;

use std::thread;

const LOG_MAX_SIZE : usize = 1024;

const LOG_WRITE_INTERVAL_IN_MILLISEC : u64 = 1000;

pub struct LogHandler<T : 'static + Log + Send> {
    log_file   : Option<String>,
    stats      : Arc<Mutex<T>>,
    write_flag : Arc<AtomicBool>,
}

#[derive(Clone, Copy, PartialEq)]
pub enum ErrorKind {
    ParseError,
}

#[derive(Clone)]
pub struct LogError {
    kind : ErrorKind,
    path : String,
}

impl fmt::Display for LogError {
    fn fmt(&self, f : &mut fmt::Formatter) -> fmt::Result {
        use self::ErrorKind::*;
        match self.kind {
            ParseError => write!(f, "failed to parse log file \"{}\"", self.path),
        }
    }
}

impl LogError {
    pub fn new(kind : ErrorKind, path : &str) -> LogError {
        LogError {
            kind,
            path : String::from(path),
        }
    }
}

pub fn to_err(e : LogError) -> super::Error {
    use super::{Error, ErrorKind};
    Error::new(ErrorKind::LogError(e))
}

pub trait Log {
    fn serialize(&self) -> String;

    fn deserialize(&mut self, &[u8]) -> Result<(), ()>;

    fn read_from_file(&mut self, log_file : &str) -> Result<(), Error> {
        let mut reader = FileReader::new(log_file,
                                         FileReaderParam { write    : false,
                                                           buffered : false  })?;
        let mut buffer : [u8; LOG_MAX_SIZE] = [0; LOG_MAX_SIZE];
        let _len_read = reader.read(&mut buffer)?;

        match self.deserialize(&buffer) {
            Ok(())  => Ok(()),
            Err(()) => Err(to_err(LogError::new(ErrorKind::ParseError, log_file))),
        }
    }

    fn write_to_file(&self, log_file : &str) -> Result<(), Error> {
        let mut writer = FileWriter::new(log_file,
                                         FileWriterParam { read     : false,
                                                           append   : false,
                                                           buffered : false  })?;
        let output = self.serialize();

        let _len_written = writer.write(output.as_bytes())?;

        Ok(())
    }
}

impl<T : 'static + Log + Send> LogHandler<T> {
    pub fn new(log_file : Option<&str>,
               stats    : &Arc<Mutex<T>>) -> LogHandler<T> {
        let log_file = match log_file {
            None         => None,
            Some(ref lg) => Some(lg.to_string()),
        };
        let write_flag = Arc::new(AtomicBool::new(true));
        let runner_write_flag = Arc::clone(&write_flag);
        thread::spawn(move || {
            loop {
                runner_write_flag.store(true, Ordering::Relaxed);

                thread::sleep(Duration::from_millis(LOG_WRITE_INTERVAL_IN_MILLISEC));
            }
        });
        LogHandler {
            log_file,
            stats    : Arc::clone(stats),
            write_flag,
        }
    }

    pub fn read_from_file(&self) -> Result<(), Error> {
        use super::ErrorKind;
        use super::file_error;

        match self.log_file {
            None         => Ok(()),
            Some(ref lg) => {
                let res = self.stats.lock().unwrap().read_from_file(lg);

                if let Err(ref e) = res {
                    if let ErrorKind::FileError(ref fe) = e.kind {
                        if let file_error::ErrorKind::NotFound = fe.kind {
                            return Ok(());
                        }
                    }
                }
                res
            }
        }
    }

    pub fn write_to_file(&self, force_write : bool) -> Result<(), Error> {
        if force_write || self.write_flag.swap(false, Ordering::SeqCst) {
            match self.log_file {
                None        => {},
                Some(ref x) => self.stats.lock().unwrap().write_to_file(x)?,
            }
        }

        Ok(())
    }
}