tinylog 4.0.2

A logger for my personal projects.
use crate::{
	Logger, PrefixOptions,
	util::{Indented, StringLike, with_local_string},
};
use log::{Log, kv};
use std::{fmt, io};

#[cfg(feature = "timestamps")]
use std::time::SystemTime;

impl<T: io::Write + Send + Sync + 'static> Log for Logger<T> {
	fn enabled(&self, _: &log::Metadata) -> bool {
		true
	}

	fn flush(&self) {
		#[allow(unused_mut)]
		let mut output = self.output.lock();
		#[cfg(not(feature = "parking_lot"))]
		let mut output = output.unwrap_or_else(|e| e.into_inner());
		output.flush().expect("failed to flush log output");
	}

	fn log(&self, record: &log::Record) {
		#[cfg(feature = "timestamps")]
		let time = SystemTime::now();

		with_local_string(move |mut buf| {
			buf.clear();

			self.write_prefix(
				&mut buf,
				&record.into(),
				&PrefixOptions {
					align: true,
					#[cfg(feature = "timestamps")]
					time: Some(time),
				},
			);

			let mut indented = Indented::new(&mut buf, 8);
			let args = record.args();
			match args.as_str() {
				Some(str) if !str.is_empty() => {
					indented.reserve(1 + str.len());
					indented.push('\n');
					indented.push_str(str);
				},
				None => {
					indented.push('\n');
					fmt::Write::write_fmt(&mut indented, *args).expect("fmt error");
				},
				_ => (),
			}

			record
				.key_values()
				.visit(&mut KeyValueVisitor(indented))
				.expect("key value visitor failed");

			buf.push('\n');
			#[allow(unused_mut)]
			let mut output = self.output.lock();
			#[cfg(not(feature = "parking_lot"))]
			let mut output = output.unwrap_or_else(|e| e.into_inner());
			output.write_all(buf.as_bytes()).expect("io error");
		})
	}
}

struct KeyValueVisitor<T: StringLike + fmt::Write>(Indented<T>);

impl<'kvs, T: StringLike + fmt::Write> kv::VisitSource<'kvs> for KeyValueVisitor<T> {
	fn visit_pair(&mut self, key: kv::Key<'kvs>, value: kv::Value<'kvs>) -> Result<(), kv::Error> {
		let key_name = key.as_str();
		self.0.reserve(key_name.len() + 3);
		self.0.push('\n');
		self.0.push_str(key_name);
		self.0.push_str(": ");
		self.0.indent += 2;
		let result = value.visit(&mut *self);
		self.0.indent -= 2;
		result
	}
}

impl<'v, T: StringLike + fmt::Write> kv::VisitValue<'v> for KeyValueVisitor<T> {
	fn visit_any(&mut self, value: kv::Value) -> Result<(), kv::Error> {
		fmt::Write::write_fmt(&mut self.0, format_args!("{value:?}"))
			.map_err(|_| kv::Error::msg("fmt error"))
	}

	fn visit_null(&mut self) -> Result<(), kv::Error> {
		self.0.push_str("null");
		Ok(())
	}

	fn visit_u64(&mut self, value: u64) -> Result<(), kv::Error> {
		self.0.push_str(itoa::Buffer::new().format(value));
		Ok(())
	}

	fn visit_i64(&mut self, value: i64) -> Result<(), kv::Error> {
		self.0.push_str(itoa::Buffer::new().format(value));
		Ok(())
	}

	fn visit_u128(&mut self, value: u128) -> Result<(), kv::Error> {
		self.0.push_str(itoa::Buffer::new().format(value));
		Ok(())
	}

	fn visit_i128(&mut self, value: i128) -> Result<(), kv::Error> {
		self.0.push_str(itoa::Buffer::new().format(value));
		Ok(())
	}

	fn visit_f64(&mut self, value: f64) -> Result<(), kv::Error> {
		self.0.push_str(zmij::Buffer::new().format(value));
		Ok(())
	}

	fn visit_bool(&mut self, value: bool) -> Result<(), kv::Error> {
		self.0.push_str(if value { "true" } else { "false" });
		Ok(())
	}

	fn visit_str(&mut self, value: &str) -> Result<(), kv::Error> {
		self.0.push_str(value);
		Ok(())
	}

	fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), kv::Error> {
		self.0.push_str(value);
		Ok(())
	}

	fn visit_char(&mut self, value: char) -> Result<(), kv::Error> {
		self.0.push(value);
		Ok(())
	}
}