1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
use std::{
    error::Error,
    ffi::CString,
    fmt::{Display, Formatter, Result as FmtResult},
};

use slog::{Drain, Level, OwnedKVList, Record};

use crate::get_callback;

fn basic_log<S>(ai_id: i32, message: S) -> Result<(), Box<dyn Error>>
where
    S: Into<Vec<u8>>,
{
    let log_func = get_callback!(ai_id, Log_log)?;

    let c_message = CString::new(message)?;

    Ok(unsafe { log_func(ai_id, c_message.as_ptr()) })
}

fn basic_exception_log<S>(
    ai_id: i32,
    message: S,
    level: i32,
    die: bool,
) -> Result<(), Box<dyn Error>>
where
    S: Into<Vec<u8>>,
{
    let log_exception_func = get_callback!(ai_id, Log_exception)?;

    let c_message = CString::new(message)?;

    Ok(unsafe { log_exception_func(ai_id, c_message.as_ptr(), level, die) })
}

pub struct AILogger {
    ai_id: i32,
    level: Level,
    file_info: bool,
}

impl AILogger {
    pub fn new(ai_id: i32) -> Self {
        Self {
            ai_id,
            level: Level::Warning,
            file_info: false,
        }
    }

    pub fn with_level(self, level: Level) -> Self {
        Self { level, ..self }
    }

    pub fn with_file_info(self, file_info: bool) -> Self {
        Self { file_info, ..self }
    }
}

#[derive(Copy, Clone, Debug)]
pub struct AILoggerOk {}

#[derive(Copy, Clone, Debug)]
pub struct AILoggerErr {}

impl Error for AILoggerErr {}

impl Display for AILoggerErr {
    fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
        write!(fmt, "{:?}", self)
    }
}

impl Drain for AILogger {
    type Ok = AILoggerOk;
    type Err = AILoggerErr;

    fn log(&self, record: &Record, values: &OwnedKVList) -> Result<Self::Ok, Self::Err> {
        let message = if self.file_info {
            format!(
                "{} [{}::{} ({}:{})]: {}",
                record.level().to_string(),
                record.module(),
                record.function(),
                record.file(),
                record.line(),
                record.msg()
            )
        } else {
            format!("{}: {}", record.level().to_string(), record.msg())
        };
        if record.level() < self.level {
            match record.level() {
                Level::Debug | Level::Trace | Level::Info => {
                    basic_log(self.ai_id, message).unwrap()
                }
                Level::Warning => basic_exception_log(self.ai_id, message, 3, false).unwrap(),
                Level::Error => basic_exception_log(self.ai_id, message, 6, false).unwrap(),
                Level::Critical => basic_exception_log(self.ai_id, message, 9, true).unwrap(),
            }
        }

        Ok(AILoggerOk {})
    }
}