doric 0.1.0

A highly simple asynchronous logging lib for Rust.
Documentation
use std::fs;
use std::fs::OpenOptions;
use std::io::prelude::*;
use std::path::Path;

use super::LoggerConfig;
use crate::encode::parser::Message;
use crate::runtime::append::Appender;

#[derive(Debug)]
pub struct Material {
    pub lc: LoggerConfig,
}

fn seg_size(dst: &String) -> u64 {
    let exist = Path::new(dst.as_str()).exists();
    if !exist {
        return 0;
    }

    let x = fs::metadata(dst.as_str()).unwrap().len();
    x
}

fn seg_rename(src: &String, dst: &String) -> std::io::Result<()> {
    let exist = Path::new(src.as_str()).exists();
    if !exist {
        return Ok(());
    }

    fs::rename(src.as_str(), dst.as_str())
}

fn seg_remove(dst: &String) -> std::io::Result<()> {
    let exist = Path::new(dst.as_str()).exists();
    if !exist {
        return Ok(());
    }

    fs::remove_file(dst.as_str())
}

fn segs_count(file_path: &String) -> Vec<String> {
    let path = Path::new(file_path.as_str());
    let parent = path.parent();
    let log_dir = parent.unwrap().display().to_string();

    let paths = fs::read_dir(log_dir.as_str()).unwrap();
    let l = file_path.len();
    let pattern = file_path.as_str();

    let mut segs: Vec<String> = vec![];
    for entry in paths {
        let entry = entry.unwrap();
        let path = entry.path();
        if !path.is_dir() {
            let s = path.display().to_string();
            let d = s.get(..l);
            match d {
                None => {}
                Some(d) => match pattern.find(d) {
                    None => {}
                    Some(_v) => {
                        segs.push(s);
                    }
                },
            }
        }
    }

    segs
}

fn segs_rotate(dst: &String, segs: &mut Vec<String>, count: usize, rename: bool) {
    let tot_len = segs.len();
    if tot_len == 0 {
        return;
    }

    segs.reverse();
    let del_len = tot_len - count;

    if del_len > 0 {
        for i in 0..del_len {
            match seg_remove(&segs[i]) {
                Ok(_v) => {}
                Err(e) => {
                    println!("segs_rotate_slow remove failed {:?}", e);
                }
            }
        }
    }

    if rename {
        for i in del_len..tot_len {
            let new_name = format!("{}.{}", dst, tot_len - i);
            match seg_rename(&segs[i].to_string(), &new_name) {
                Ok(_v) => {}
                Err(e) => {
                    println!("segs_rotate_slow rename failed {:?}", e);
                }
            }
        }
    }
}

impl Material {
    pub fn new(conf: LoggerConfig) -> Material {
        Material { lc: conf }
    }

    fn rotate(&self, current_len: usize) -> std::io::Result<()> {
        let mut tot: usize = 0;
        let exist = Path::new(self.lc.path.as_str()).exists();
        if exist {
            let seg_sz = seg_size(&self.lc.path);
            tot += seg_sz as usize + current_len;
        }

        let mut segs = segs_count(&self.lc.path);
        let seg_count = segs.len();

        if tot <= self.lc.max_size as usize && seg_count < self.lc.max_segments {
            return Ok(());
        }

        if tot > self.lc.max_size as usize {
            segs_rotate(&self.lc.path, &mut segs, self.lc.max_segments - 1, true);
        } else {
            if exist {
                segs_rotate(&self.lc.path, &mut segs, self.lc.max_segments, false);
            } else {
                segs_rotate(&self.lc.path, &mut segs, self.lc.max_segments - 1, false);
            }
        }

        Ok(())
    }

    fn output(&self, msg: &Message) {
        let data = format!(
            "{} {} {}\n",
            chrono::offset::Local::now().to_string(),
            msg.level.to_string(),
            msg.args
        );

        match self.rotate(data.len()) {
            Ok(_v) => {}
            Err(e) => {
                println!("rotate failed {:?}", e);
            }
        }

        let mut file = OpenOptions::new()
            .create(true)
            .append(true)
            .open(self.lc.path.as_str())
            .unwrap();
        file.write(data.as_bytes()).unwrap();
    }
}

impl Appender for Material {
    fn append(&self, msg: &Message) -> Result<(), String> {
        self.output(msg);
        Ok(())
    }
}