use std::rc::Rc;
use std::cell::RefCell;
use std::any::Any;
use std::collections::HashMap;
use std::time::{Instant, Duration};
use std::fmt::{self, Debug};
use std::marker::PhantomData;
use timely_container::{ContainerBuilder, PushInto};
pub struct Registry {
map: HashMap<String, (Box<dyn Any>, Box<dyn Flush>)>,
time: Instant,
}
impl Registry {
pub fn insert<CB: ContainerBuilder<Container: Default> + 'static, F: FnMut(&Duration, &mut Option<CB::Container>)+'static>(
&mut self,
name: &str,
action: F) -> Option<Box<dyn Any>>
{
let logger = Logger::<CB>::new(self.time, Duration::default(), action);
self.insert_logger(name, logger)
}
pub fn insert_logger<CB: ContainerBuilder<Container: Default> + 'static>(&mut self, name: &str, logger: Logger<CB>) -> Option<Box<dyn Any>> {
self.map.insert(name.to_owned(), (Box::new(logger.clone()), Box::new(logger))).map(|x| x.0)
}
pub fn remove(&mut self, name: &str) -> Option<Box<dyn Any>> {
self.map.remove(name).map(|x| x.0)
}
pub fn get<CB: ContainerBuilder<Container: Default> + 'static>(&self, name: &str) -> Option<Logger<CB>> {
self.map
.get(name)
.and_then(|entry| entry.0.downcast_ref::<Logger<CB>>())
.map(|x| (*x).clone())
}
pub fn new(time: Instant) -> Self {
Registry {
time,
map: HashMap::new(),
}
}
pub fn flush(&mut self) {
<Self as Flush>::flush(self);
}
}
impl Flush for Registry {
fn flush(&self) {
for value in self.map.values() {
value.1.flush();
}
}
}
pub struct Logger<CB: ContainerBuilder<Container: Default>> {
inner: Rc<RefCell<LoggerInner<CB, dyn FnMut(&Duration, &mut Option<CB::Container>)>>>,
}
impl<CB: ContainerBuilder<Container: Default>> Clone for Logger<CB> {
fn clone(&self) -> Self {
Self {
inner: Rc::clone(&self.inner)
}
}
}
impl<CB: ContainerBuilder<Container: Default> + Debug> Debug for Logger<CB> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Logger")
.field("inner", &self.inner)
.finish()
}
}
struct LoggerInner<CB: ContainerBuilder<Container: Default>, A: ?Sized + FnMut(&Duration, &mut Option<CB::Container>)> {
time: Instant,
offset: Duration,
builder: CB,
action: A,
}
impl<CB: ContainerBuilder<Container: Default>> Logger<CB> {
pub fn new<F>(time: Instant, offset: Duration, action: F) -> Self
where
F: FnMut(&Duration, &mut Option<CB::Container>)+'static
{
let inner = LoggerInner {
time,
offset,
action,
builder: CB::default(),
};
let inner = Rc::new(RefCell::new(inner));
Logger { inner }
}
pub fn log<T>(&self, event: T) where CB: PushInto<(Duration, T)> {
self.log_many(Some(event));
}
pub fn log_many<I>(&self, events: I)
where I: IntoIterator, CB: PushInto<(Duration, I::Item)>
{
self.inner.borrow_mut().log_many(events)
}
pub fn flush(&self) {
<Self as Flush>::flush(self);
}
pub fn into_typed<T>(self) -> TypedLogger<CB, T> {
self.into()
}
}
#[derive(Debug)]
pub struct TypedLogger<CB: ContainerBuilder<Container: Default>, T> {
inner: Logger<CB>,
_marker: PhantomData<T>,
}
impl<CB: ContainerBuilder<Container: Default>, T> TypedLogger<CB, T> {
pub fn log<S: Into<T>>(&self, event: S)
where
CB: PushInto<(Duration, T)>,
{
self.inner.log(event.into());
}
pub fn log_many<I>(&self, events: I)
where
I: IntoIterator, I::Item: Into<T>,
CB: PushInto<(Duration, T)>,
{
self.inner.log_many(events.into_iter().map(Into::into));
}
}
impl<CB: ContainerBuilder<Container: Default>, T> Clone for TypedLogger<CB, T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
_marker: PhantomData,
}
}
}
impl<CB: ContainerBuilder<Container: Default>, T> From<Logger<CB>> for TypedLogger<CB, T> {
fn from(inner: Logger<CB>) -> Self {
TypedLogger {
inner,
_marker: PhantomData,
}
}
}
impl<CB: ContainerBuilder<Container: Default>, T> std::ops::Deref for TypedLogger<CB, T> {
type Target = Logger<CB>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<CB: ContainerBuilder<Container: Default>, A: ?Sized + FnMut(&Duration, &mut Option<CB::Container>)> LoggerInner<CB, A> {
#[inline]
fn push(action: &mut A, time: &Duration, container: &mut CB::Container) {
let mut c = Some(std::mem::take(container));
action(time, &mut c);
if let Some(c) = c {
*container = c;
}
}
fn log_many<I>(&mut self, events: I)
where I: IntoIterator, CB: PushInto<(Duration, I::Item)>,
{
let elapsed = self.time.elapsed() + self.offset;
for event in events {
self.builder.push_into((elapsed, event));
while let Some(container) = self.builder.extract() {
Self::push(&mut self.action, &elapsed, container);
}
}
}
fn flush(&mut self) {
let elapsed = self.time.elapsed() + self.offset;
while let Some(container) = self.builder.finish() {
Self::push(&mut self.action, &elapsed, container);
}
(self.action)(&elapsed, &mut None);
}
}
impl<CB: ContainerBuilder<Container: Default>, A: ?Sized + FnMut(&Duration, &mut Option<CB::Container>)> Drop for LoggerInner<CB, A> {
fn drop(&mut self) {
self.flush();
}
}
impl<CB, A: ?Sized + FnMut(&Duration, &mut Option<CB::Container>)> Debug for LoggerInner<CB, A>
where
CB: ContainerBuilder<Container: Default> + Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LoggerInner")
.field("time", &self.time)
.field("offset", &self.offset)
.field("action", &"FnMut")
.field("builder", &self.builder)
.finish()
}
}
trait Flush {
fn flush(&self);
}
impl<CB: ContainerBuilder<Container: Default>> Flush for Logger<CB> {
fn flush(&self) {
self.inner.borrow_mut().flush()
}
}