summaryrefslogtreecommitdiff
path: root/console/src/lib.rs
blob: 578d255837f1d4a1511439c497936da98fc934d5 (plain)
use std::io::{stdout, Write};
use std::sync::mpsc::{Receiver, Sender};

use log::Level;

pub struct Console {
	messages: Receiver<ConsoleMessage>,
	sender: Sender<ConsoleMessage>,
}

pub struct ConsoleLogger(Sender<ConsoleMessage>);

pub enum ConsoleMessage {
	RuntimeLog {
		message: String,
		level: Level,
		file: String,
		line: u32,
	},
	ScriptLog {
		message: String,
		level: Level,
		file: String,
		line: u32,
	},
	FrameTime {
		timestamp: i64,
	},
	ScopeStart {
		scope_name: String,
		timestamp: i64,
	},
	ScopeEnd {
		timestamp: i64,
	},
}

impl Console {
	pub async fn new(port: u16) -> Result<Self, std::io::Error> {
		let (sender, reciever) = std::sync::mpsc::channel();
		Ok(Self {
			messages: reciever,
			sender,
		})
	}

	fn sender(&self) -> Sender<ConsoleMessage> {
		self.sender.clone()
	}

	pub fn logger(&self) -> ConsoleLogger {
		ConsoleLogger(self.sender())
	}

	pub fn flush(&self) -> std::io::Result<()> {
		let mut stdout = stdout().lock();

		while let Ok(message) = self.messages.try_recv() {
			let message = match message {
				ConsoleMessage::RuntimeLog {
					message,
					level,
					file,
					line,
				} => {
					let message = message.replace('\n', "\\n");
					format!("runtimelog {level} {file}:{line} {message}\n")
				}
				ConsoleMessage::ScriptLog {
					message,
					level,
					file,
					line,
				} => {
					let message = message.replace('\n', "\\n");
					format!("scriptlog {level} {file}:{line} {message}\n")
				}
				ConsoleMessage::FrameTime {
					timestamp: unix_timestamp,
				} => {
					format!("frametime {unix_timestamp}")
				}
				ConsoleMessage::ScopeStart {
					scope_name,
					timestamp: unix_timestamp,
				} => {
					format!("scopestart {scope_name} {unix_timestamp}")
				}
				ConsoleMessage::ScopeEnd {
					timestamp: unix_timestamp,
				} => {
					format!("scopeend {unix_timestamp}")
				}
			};

			stdout.write_all(message.as_bytes())?;
			stdout.write_all(b"\n")?;
		}

		stdout.flush()?;

		Ok(())
	}
}

impl ConsoleLogger {
	pub fn send(&self, message: ConsoleMessage) {
		_ = self.0.send(message);
	}
}

impl log::Log for ConsoleLogger {
	fn enabled(&self, _: &log::Metadata) -> bool {
		true
	}

	fn log(&self, record: &log::Record) {
		let _ = self.0.send(ConsoleMessage::RuntimeLog {
			message: record.args().to_string(),
			level: record.level(),
			file: record.file().map(str::to_string).unwrap_or_default(),
			line: record.line().unwrap_or_default(),
		});
	}

	fn flush(&self) {}
}