Решение на Logging от Андрей

Обратно към всички решения

Към профила на Андрей

Резултати

  • 20 точки от тестове
  • 0 бонус точки
  • 20 точки общо
  • 15 успешни тест(а)
  • 0 неуспешни тест(а)

Код

use std::cell::RefCell;
use std::cmp::{PartialOrd, Ordering};
use std::collections::BinaryHeap;
use std::fmt;
use std::io::{self, Write};
use std::rc::Rc;
use std::time::Instant;
pub trait Logger {
fn push(&mut self, time: Instant, text: &str);
fn try_flush(&mut self) -> io::Result<()>;
fn log(&mut self, text: &str) {
self.push(Instant::now(), text)
}
fn flush(&mut self) {
if let Err(e) = self.try_flush() {
eprintln!("Logger error: {}", e);
}
}
}
pub struct BufferedLogger<W: Write> {
buffer_size: usize,
out: Rc<RefCell<W>>,
buffer: Rc<RefCell<BinaryHeap<Entry>>>,
}
impl<W: Write> Clone for BufferedLogger<W> {
fn clone(&self) -> Self {
BufferedLogger {
buffer_size: self.buffer_size,
out: self.out.clone(),
buffer: self.buffer.clone(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Ord)]
pub struct Entry {
pub time: Instant,
pub text: String,
}
impl PartialOrd for Entry {
/// Note: Order is in reverse, so that we can iterate by earliest-first using a BinaryHeap.
fn partial_cmp(&self, other: &Entry) -> Option<Ordering> {
other.time.partial_cmp(&self.time)
}
}
impl fmt::Display for Entry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(&self.text)
}
}
impl<W: Write> BufferedLogger<W> {
pub fn new(out: W, buffer_size: usize) -> Self {
let out = Rc::new(RefCell::new(out));
let buffer = Rc::new(RefCell::new(BinaryHeap::with_capacity(buffer_size + 1)));
BufferedLogger { buffer_size, out, buffer }
}
pub fn buffered_entries(&self) -> Vec<String> {
self.buffer.
borrow_mut().
clone().
into_sorted_vec().
iter().
rev().
map(|e| e.to_string()).
collect()
}
}
impl<W: Write> Logger for BufferedLogger<W> {
fn push(&mut self, time: Instant, text: &str) {
let text = String::from(text);
self.buffer.borrow_mut().push(Entry { time, text });
if self.buffer.borrow().len() >= self.buffer_size {
self.flush();
}
}
fn try_flush(&mut self) -> io::Result<()> {
let mut buffer = self.buffer.borrow_mut();
let mut out = self.out.borrow_mut();
while let Some(entry) = buffer.pop() {
out.write_all(entry.to_string().as_bytes())?;
out.write_all(b"\n")?;
}
Ok(())
}
}
pub struct MultiLogger {
loggers: Vec<Box<dyn Logger>>,
}
impl MultiLogger {
pub fn new() -> Self {
MultiLogger { loggers: Vec::new() }
}
pub fn log_to<L: Logger + 'static>(&mut self, logger: L) {
self.loggers.push(Box::new(logger));
}
}
impl Logger for MultiLogger {
fn push(&mut self, time: Instant, text: &str) {
for mut logger in self.loggers.iter_mut() { logger.push(time, text) }
}
fn try_flush(&mut self) -> io::Result<()> {
for mut logger in self.loggers.iter_mut() { logger.try_flush()? }
Ok(())
}
}
pub struct ScopedLogger<L: Logger> {
tag: String,
base_logger: L
}
impl<L: Logger> ScopedLogger<L> {
pub fn new(tag: &str, base_logger: L) -> Self {
let tag = String::from(tag);
ScopedLogger { tag, base_logger }
}
}
impl<L: Logger> Logger for ScopedLogger<L> {
fn push(&mut self, time: Instant, text: &str) {
self.base_logger.push(time, &format!("[{}] {}", self.tag, text));
}
fn try_flush(&mut self) -> io::Result<()> {
self.base_logger.try_flush()
}
}

Лог от изпълнението

Compiling solution v0.1.0 (/tmp/d20190123-22631-1c58tbm/solution)
    Finished dev [unoptimized + debuginfo] target(s) in 5.48s
     Running target/debug/deps/solution-2e785d603b538f71

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

     Running target/debug/deps/solution_test-29808948fb50ed3a

running 15 tests
test solution_test::test_automatic_flushing_when_buffer_limit_is_reached ... ok
test solution_test::test_automatic_flushing_when_zero_buffer_limit ... ok
test solution_test::test_basic_log ... ok
test solution_test::test_basic_push ... ok
test solution_test::test_cloning_a_logger_shares_a_buffer ... ok
test solution_test::test_cloning_a_logger_shares_their_io ... ok
test solution_test::test_erroring_io ... ok
test solution_test::test_flushing_the_buffer ... ok
test solution_test::test_logger_combinations ... ok
test solution_test::test_multilogger_logs_and_flushes_when_needed ... ok
test solution_test::test_multilogger_logs_to_several_ios ... ok
test solution_test::test_reordering_logs_in_buffer ... ok
test solution_test::test_reordering_logs_in_io ... ok
test solution_test::test_scoped_logger ... ok
test solution_test::test_scoped_logger_with_a_string_tag ... ok

test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

   Doc-tests solution

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

История (1 версия и 0 коментара)

Андрей обнови решението на 10.12.2018 18:36 (преди почти 2 години)

+use std::cell::RefCell;
+use std::cmp::{PartialOrd, Ordering};
+use std::collections::BinaryHeap;
+use std::fmt;
+use std::io::{self, Write};
+use std::rc::Rc;
+use std::time::Instant;
+
+pub trait Logger {
+ fn push(&mut self, time: Instant, text: &str);
+ fn try_flush(&mut self) -> io::Result<()>;
+
+ fn log(&mut self, text: &str) {
+ self.push(Instant::now(), text)
+ }
+
+ fn flush(&mut self) {
+ if let Err(e) = self.try_flush() {
+ eprintln!("Logger error: {}", e);
+ }
+ }
+}
+
+pub struct BufferedLogger<W: Write> {
+ buffer_size: usize,
+ out: Rc<RefCell<W>>,
+ buffer: Rc<RefCell<BinaryHeap<Entry>>>,
+}
+
+impl<W: Write> Clone for BufferedLogger<W> {
+ fn clone(&self) -> Self {
+ BufferedLogger {
+ buffer_size: self.buffer_size,
+ out: self.out.clone(),
+ buffer: self.buffer.clone(),
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Ord)]
+pub struct Entry {
+ pub time: Instant,
+ pub text: String,
+}
+
+impl PartialOrd for Entry {
+ /// Note: Order is in reverse, so that we can iterate by earliest-first using a BinaryHeap.
+ fn partial_cmp(&self, other: &Entry) -> Option<Ordering> {
+ other.time.partial_cmp(&self.time)
+ }
+}
+
+impl fmt::Display for Entry {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str(&self.text)
+ }
+}
+
+impl<W: Write> BufferedLogger<W> {
+ pub fn new(out: W, buffer_size: usize) -> Self {
+ let out = Rc::new(RefCell::new(out));
+ let buffer = Rc::new(RefCell::new(BinaryHeap::with_capacity(buffer_size + 1)));
+
+ BufferedLogger { buffer_size, out, buffer }
+ }
+
+ pub fn buffered_entries(&self) -> Vec<String> {
+ self.buffer.
+ borrow_mut().
+ clone().
+ into_sorted_vec().
+ iter().
+ rev().
+ map(|e| e.to_string()).
+ collect()
+ }
+}
+
+impl<W: Write> Logger for BufferedLogger<W> {
+ fn push(&mut self, time: Instant, text: &str) {
+ let text = String::from(text);
+ self.buffer.borrow_mut().push(Entry { time, text });
+
+ if self.buffer.borrow().len() >= self.buffer_size {
+ self.flush();
+ }
+ }
+
+ fn try_flush(&mut self) -> io::Result<()> {
+ let mut buffer = self.buffer.borrow_mut();
+ let mut out = self.out.borrow_mut();
+
+ while let Some(entry) = buffer.pop() {
+ out.write_all(entry.to_string().as_bytes())?;
+ out.write_all(b"\n")?;
+ }
+
+ Ok(())
+ }
+}
+
+pub struct MultiLogger {
+ loggers: Vec<Box<dyn Logger>>,
+}
+
+impl MultiLogger {
+ pub fn new() -> Self {
+ MultiLogger { loggers: Vec::new() }
+ }
+
+ pub fn log_to<L: Logger + 'static>(&mut self, logger: L) {
+ self.loggers.push(Box::new(logger));
+ }
+}
+
+impl Logger for MultiLogger {
+ fn push(&mut self, time: Instant, text: &str) {
+ for mut logger in self.loggers.iter_mut() { logger.push(time, text) }
+ }
+
+ fn try_flush(&mut self) -> io::Result<()> {
+ for mut logger in self.loggers.iter_mut() { logger.try_flush()? }
+ Ok(())
+ }
+}
+
+pub struct ScopedLogger<L: Logger> {
+ tag: String,
+ base_logger: L
+}
+
+impl<L: Logger> ScopedLogger<L> {
+ pub fn new(tag: &str, base_logger: L) -> Self {
+ let tag = String::from(tag);
+ ScopedLogger { tag, base_logger }
+ }
+}
+
+impl<L: Logger> Logger for ScopedLogger<L> {
+ fn push(&mut self, time: Instant, text: &str) {
+ self.base_logger.push(time, &format!("[{}] {}", self.tag, text));
+ }
+
+ fn try_flush(&mut self) -> io::Result<()> {
+ self.base_logger.try_flush()
+ }
+}