use crate::lazy_static::lazy_static;
use crate::parking_lot::Mutex;
use std::fmt::{Debug, Display};
use crate::instant::Instant;
#[cfg(not(target_arch = "wasm32"))]
use std::io::{self, Write};
use std::sync::mpsc::Sender;
use std::time::Duration;
#[cfg(target_arch = "wasm32")]
use crate::wasm_bindgen::{self, prelude::*};
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}
pub struct LogMessage {
pub kind: MessageKind,
pub content: String,
pub time: Duration,
}
lazy_static! {
static ref LOG: Mutex<Log> = Mutex::new(Log {
#[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))]
file: std::fs::File::create("fyrox.log").unwrap(),
verbosity: MessageKind::Information,
listeners: Default::default(),
time_origin: Instant::now()
});
}
#[derive(Copy, Clone, PartialOrd, PartialEq, Eq, Ord, Hash)]
#[repr(u32)]
pub enum MessageKind {
Information = 0,
Warning = 1,
Error = 2,
}
impl MessageKind {
fn as_str(self) -> &'static str {
match self {
MessageKind::Information => "[INFO]: ",
MessageKind::Warning => "[WARNING]: ",
MessageKind::Error => "[ERROR]: ",
}
}
}
pub struct Log {
#[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))]
file: std::fs::File,
verbosity: MessageKind,
listeners: Vec<Sender<LogMessage>>,
time_origin: Instant,
}
impl Log {
fn write_internal<S>(&mut self, kind: MessageKind, message: S)
where
S: AsRef<str>,
{
let mut msg = message.as_ref().to_owned();
if kind as u32 >= self.verbosity as u32 {
for listener in self.listeners.iter() {
let _ = listener.send(LogMessage {
kind,
content: msg.clone(),
time: Instant::now() - self.time_origin,
});
}
msg.insert_str(0, kind.as_str());
#[cfg(target_arch = "wasm32")]
{
log(&msg);
}
#[cfg(all(not(target_os = "android"), not(target_arch = "wasm32")))]
{
let _ = io::stdout().write_all(msg.as_bytes());
let _ = self.file.write_all(msg.as_bytes());
}
#[cfg(target_os = "android")]
{
let _ = io::stdout().write_all(msg.as_bytes());
}
}
}
fn writeln_internal<S>(&mut self, kind: MessageKind, message: S)
where
S: AsRef<str>,
{
let mut msg = message.as_ref().to_owned();
msg.push('\n');
self.write_internal(kind, msg)
}
pub fn write<S>(kind: MessageKind, msg: S)
where
S: AsRef<str>,
{
LOG.lock().write_internal(kind, msg);
}
pub fn writeln<S>(kind: MessageKind, msg: S)
where
S: AsRef<str>,
{
LOG.lock().writeln_internal(kind, msg);
}
pub fn info<S>(msg: S)
where
S: AsRef<str>,
{
Self::writeln(MessageKind::Information, msg)
}
pub fn warn<S>(msg: S)
where
S: AsRef<str>,
{
Self::writeln(MessageKind::Warning, msg)
}
pub fn err<S>(msg: S)
where
S: AsRef<str>,
{
Self::writeln(MessageKind::Error, msg)
}
pub fn set_verbosity(kind: MessageKind) {
LOG.lock().verbosity = kind;
}
pub fn add_listener(listener: Sender<LogMessage>) {
LOG.lock().listeners.push(listener)
}
pub fn verify<T, E>(result: Result<T, E>)
where
E: Debug,
{
if let Err(e) = result {
Self::writeln(
MessageKind::Error,
format!("Operation failed! Reason: {:?}", e),
);
}
}
pub fn verify_message<S, T, E>(result: Result<T, E>, msg: S)
where
E: Debug,
S: Display,
{
if let Err(e) = result {
Self::writeln(MessageKind::Error, format!("{}. Reason: {:?}", msg, e));
}
}
}