romp 0.5.2

STOMP server and WebSockets platform
Documentation
//! Not the most efficient possible message logger, uses synchronous IO, this is acceptable in
//! many situations in Linux since `write()` is buffered by the kernel and returns quite quick.
//! There is no guarantee that a message is written when the `store()` method returns.

use std::fs::{File, OpenOptions};
use std::io;
use std::io::Write;

use chrono::SecondsFormat;

use crate::message::stomp_message::StompMessage;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Mutex;

// message logging

pub trait MLog {
    fn store(&self, user: &str, message: &StompMessage) -> io::Result<usize>;
}


pub struct SyncMLog {
    file: Mutex<File>,
    err: AtomicBool
}

impl SyncMLog {
    pub fn new(file_name: &str) -> Result<SyncMLog, i32> {
        return match OpenOptions::new()
            .append(true)
            .write(true)
            .create(true)
            .open(file_name) {
            Ok(file) => {
                Ok(SyncMLog {
                    file: Mutex::new(file),
                    err: AtomicBool::from(false)
                })
            }
            _ => Err(-1)
        }
    }

}

impl MLog for SyncMLog {

    fn store(&self, user: &str, message: &StompMessage) -> io::Result<usize> {
        if self.err.load(Ordering::Relaxed) {
            return Err(io::Error::from(io::ErrorKind::Other));
        }
        let header = format!("MSG {} {} {} {} {}\n", message.id, message.header_len(), message.body_len(), message.timestamp.to_rfc3339_opts(SecondsFormat::Millis, true), user);

        // Writing to a memory buffer and a single `write()` syscall might be faster.
        let mut file = self.file.lock().unwrap();
        if let Err(e) = file.write(header.as_bytes()) {
            self.err.store(true, Ordering::Relaxed);
            return Err(e);
        }
        // causes a copy
        if let Err(e) = file.write(message.hdrs_as_string().as_bytes()) {
            self.err.store(true, Ordering::Relaxed);
            return Err(e);
        }
        // causes a copy
        if let Err(e) = file.write(message.combine().as_slice()) {
            self.err.store(true, Ordering::Relaxed);
            return Err(e);
        }
        if let Err(e) = file.write(&[0 as u8, b'\n']) {
            self.err.store(true, Ordering::Relaxed);
            return Err(e);
        }

        Ok(0)
    }
}


#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_happy() {
        let mut message = StompMessage::new_send(b"some data", 0);
        message.add_header("some", "header");

        let mlog = SyncMLog::new("/tmp/test.mlog").unwrap();
        mlog.store("teknopaul", &message).expect("mlog write failed");
    }
}