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;
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);
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);
}
if let Err(e) = file.write(message.hdrs_as_string().as_bytes()) {
self.err.store(true, Ordering::Relaxed);
return Err(e);
}
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");
}
}