Решение на Logging от Гергана Грудева

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

Към профила на Гергана Грудева

Резултати

  • 17 точки от тестове
  • 0 бонус точки
  • 17 точки общо
  • 13 успешни тест(а)
  • 2 неуспешни тест(а)

Код

use std::time::Instant;
use std::io;
use std::io::Write;
use std::collections::BTreeMap;
use std::rc::Rc;
use std::cell::RefCell;
use std::time::Duration;
pub trait Logger {
fn push(&mut self, time: Instant, text: &str);
fn log(&mut self, text: &str) {
self.push(Instant::now(), text)
}
fn try_flush(&mut self) -> io::Result<()>;
fn flush(&mut self) {
match self.try_flush() {
Ok(val) => val,
Err(err) => eprintln!("{}", err)
}
}
}
pub struct BufferedLogger<W: Write> {
buffer: Rc<RefCell<BTreeMap<Instant, String>>>,
out: Rc<RefCell<W>>,
max_size: usize
}

Интересно решение за структура от данни. Един потенциален проблем е, че не можеш да имаш два входа с един и същ timestamp. Предвид, че не сме дефинирали, че е необходимо, да кажем, че съм ок с това.

impl<W: Write> BufferedLogger<W> {
pub fn new(out: W, buffer_size: usize) -> Self {
BufferedLogger{out: Rc::new(RefCell::new(out)), buffer: Rc::new(RefCell::new(BTreeMap::new())), max_size: buffer_size}
}
pub fn buffered_entries(&self) -> Vec<String> {
self.buffer.borrow_mut().values().cloned().collect()
}
}
impl<W: Write> Clone for BufferedLogger<W> {
fn clone(&self) -> Self {
BufferedLogger{out: self.out.clone(), buffer: self.buffer.clone(), max_size: self.max_size}
}
}
impl<W: Write> Logger for BufferedLogger<W> {
fn push(&mut self, time: Instant, text: &str) {
if self.buffer.borrow_mut().len() == self.max_size - 1{
self.flush();
} else {
self.buffer.borrow_mut().insert(time, String::from(text));
}
}

Това "гълта" стойности. Ако влезеш в клона, в който flush-ваш, не влизаш в клона, в който добавяш нова стойност. Идеята беше да си логваш неща нормално, и в случай, че се мине някаква граница, след това да се flush-не. Няма логика push-а да е в else-а в тази ситуация.

fn try_flush(&mut self) -> io::Result<()> {
for(_, log) in self.buffer.borrow_mut().iter() {
if let Err(err) = self.out.borrow_mut().write(format!("{}\n", log).as_bytes()) {
eprintln!("{}", err)
}
}
self.buffer.borrow_mut().clear();
self.out.borrow_mut().flush()
}
}
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 l in &mut self.loggers {
l.push(time, text);
}
}
fn try_flush(&mut self) -> io::Result<()> {
for l in &mut self.loggers {
if let Err(err) = l.try_flush() {
eprintln!("{}", err)
}
}
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 {
ScopedLogger{tag: String::from(tag), base_logger: base_logger}
}
}
impl<L: Logger> Logger for ScopedLogger<L> {
fn push(&mut self, time: Instant, text: &str) {
let new_text = format!("[{}] {}", self.tag, text);
self.base_logger.push(time, &new_text[..]);
}
fn try_flush(&mut self) -> io::Result<()> {
self.base_logger.try_flush()
}
}

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

Compiling solution v0.1.0 (/tmp/d20190123-22631-7tuuqo/solution)
warning: unused import: `std::time::Duration`
 --> src/lib.rs:7:5
  |
7 | use std::time::Duration;
  |     ^^^^^^^^^^^^^^^^^^^
  |
  = note: #[warn(unused_imports)] on by default

warning: unused import: `std::time::Duration`
 --> src/lib.rs:7:5
  |
7 | use std::time::Duration;
  |     ^^^^^^^^^^^^^^^^^^^
  |
  = note: #[warn(unused_imports)] on by default

    Finished dev [unoptimized + debuginfo] target(s) in 5.00s
     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 ... FAILED
test solution_test::test_automatic_flushing_when_zero_buffer_limit ... FAILED
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

failures:

---- solution_test::test_automatic_flushing_when_buffer_limit_is_reached stdout ----
thread 'solution_test::test_automatic_flushing_when_buffer_limit_is_reached' panicked at 'assertion failed: `(left == right)`
  left: `"One\nTwo\n"`,
 right: `"One\nTwo\nThree\n"`', tests/solution_test.rs:200:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.

---- solution_test::test_automatic_flushing_when_zero_buffer_limit stdout ----
thread 'solution_test::test_automatic_flushing_when_zero_buffer_limit' panicked at 'attempt to subtract with overflow', /tmp/d20190123-22631-7tuuqo/solution/src/lib.rs:53:46


failures:
    solution_test::test_automatic_flushing_when_buffer_limit_is_reached
    solution_test::test_automatic_flushing_when_zero_buffer_limit

test result: FAILED. 13 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out

error: test failed, to rerun pass '--test solution_test'

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

Гергана обнови решението на 18.12.2018 10:00 (преди над 1 година)

+use std::time::Instant;
+use std::io;
+use std::io::Write;
+use std::collections::BTreeMap;
+use std::rc::Rc;
+use std::cell::RefCell;
+use std::time::Duration;
+
+pub trait Logger {
+
+ fn push(&mut self, time: Instant, text: &str);
+
+ fn log(&mut self, text: &str) {
+ self.push(Instant::now(), text)
+ }
+
+ fn try_flush(&mut self) -> io::Result<()>;
+
+ fn flush(&mut self) {
+ match self.try_flush() {
+ Ok(val) => val,
+ Err(err) => eprintln!("{}", err)
+ }
+ }
+}
+
+pub struct BufferedLogger<W: Write> {
+ buffer: Rc<RefCell<BTreeMap<Instant, String>>>,
+ out: Rc<RefCell<W>>,
+ max_size: usize
+}

Интересно решение за структура от данни. Един потенциален проблем е, че не можеш да имаш два входа с един и същ timestamp. Предвид, че не сме дефинирали, че е необходимо, да кажем, че съм ок с това.

+
+impl<W: Write> BufferedLogger<W> {
+
+ pub fn new(out: W, buffer_size: usize) -> Self {
+ BufferedLogger{out: Rc::new(RefCell::new(out)), buffer: Rc::new(RefCell::new(BTreeMap::new())), max_size: buffer_size}
+ }
+
+ pub fn buffered_entries(&self) -> Vec<String> {
+ self.buffer.borrow_mut().values().cloned().collect()
+ }
+}
+
+impl<W: Write> Clone for BufferedLogger<W> {
+ fn clone(&self) -> Self {
+ BufferedLogger{out: self.out.clone(), buffer: self.buffer.clone(), max_size: self.max_size}
+ }
+}
+
+impl<W: Write> Logger for BufferedLogger<W> {
+
+ fn push(&mut self, time: Instant, text: &str) {
+ if self.buffer.borrow_mut().len() == self.max_size - 1{
+ self.flush();
+ } else {
+ self.buffer.borrow_mut().insert(time, String::from(text));
+ }
+ }

Това "гълта" стойности. Ако влезеш в клона, в който flush-ваш, не влизаш в клона, в който добавяш нова стойност. Идеята беше да си логваш неща нормално, и в случай, че се мине някаква граница, след това да се flush-не. Няма логика push-а да е в else-а в тази ситуация.

+
+ fn try_flush(&mut self) -> io::Result<()> {
+ for(_, log) in self.buffer.borrow_mut().iter() {
+ if let Err(err) = self.out.borrow_mut().write(format!("{}\n", log).as_bytes()) {
+ eprintln!("{}", err)
+ }
+ }
+ self.buffer.borrow_mut().clear();
+ self.out.borrow_mut().flush()
+ }
+}
+
+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 l in &mut self.loggers {
+ l.push(time, text);
+ }
+ }
+
+ fn try_flush(&mut self) -> io::Result<()> {
+ for l in &mut self.loggers {
+ if let Err(err) = l.try_flush() {
+ eprintln!("{}", err)
+ }
+ }
+ 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 {
+ ScopedLogger{tag: String::from(tag), base_logger: base_logger}
+ }
+}
+
+impl<L: Logger> Logger for ScopedLogger<L> {
+ fn push(&mut self, time: Instant, text: &str) {
+ let new_text = format!("[{}] {}", self.tag, text);
+ self.base_logger.push(time, &new_text[..]);
+ }
+
+ fn try_flush(&mut self) -> io::Result<()> {
+ self.base_logger.try_flush()
+ }
+}