Решение на Logging от Георги Петков

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

Към профила на Георги Петков

Резултати

  • 8 точки от тестове
  • 0 бонус точки
  • 8 точки общо
  • 6 успешни тест(а)
  • 9 неуспешни тест(а)

Код

use std::time::Instant;
use std::io;
use std::rc::Rc;
use std::cell::{RefCell,Ref, RefMut};
use std::io::Write;
//
// LOGGER
//
pub trait Logger {
// Метод, който добавя нов запис за логване. Първия аргумент, от тип `std::time::Instant`, е
// момента във времето, който се асоциира със събитието. Обикновено ще се ползва метода `log`
// директно, който запълва този параметър, но метода `push` ще е удобен за тестване на
// логиката.
//
// Втория аргумент е низа, който ще се логва.
//
fn push(&mut self, time: Instant, text: &str);
// Метод който ще работи като `push`, с тази разлика, че директно използва `Instant::now()` за
// да вземе текущ timestamp.
//
fn log(&mut self, text: &str) {
self.push(Instant::now(), text);
}
// Метод, който записва нещата от вътрешния буфер към някакъв външен носител -- файл, сокет,
// стандартния изход. В случай на имплементация, която няма нужда от този метод, винаги може да
// се имплементира като просто `Ok(())`.
//
fn try_flush(&mut self) -> io::Result<()>;
// Метод, който прави същото като по-горния, но не връща грешка. Вижте по-долу за бележки за
// Error handling-а, който очакваме.
//
fn flush(&mut self) {
if let Err(e) = self.try_flush() {
eprintln!("{}", e);
}
}
}
//
// SHARED
//
struct Shared<T> {
v: Rc<RefCell<T>>
}
impl <T> Shared<T> {
fn new(t: T)-> Shared<T> {
Shared{v: Rc::new(RefCell::new(t))}
}
}
impl <T> Shared<T> {
fn borrow(&self) -> Ref<T> {
self.v.borrow()
}
fn borrow_mut(&self) -> RefMut<T> {
self.v.borrow_mut()
}
}
impl <T> Clone for Shared<T> {
fn clone(&self) -> Self {
Shared {
v: self.v.clone(),
}
}
}
//
// ENTRIE
//
struct Entrie {
text: String,
time: Instant,
}
impl Entrie {
fn new (text: String, time: Instant) -> Self {
Entrie {text, time}
}
fn time (&self) -> &Instant {
&self.time
}
}
//
// BUFFEREDLOGGER
//
pub struct BufferedLogger<W: Write> {
buffer: Shared<Vec<Entrie>>,
out: Shared<W>,
buffer_size: usize,
}
impl<W: Write> BufferedLogger<W> {
// Конструира структура, която ще пази записи в буфер с размер `buffer_size`, и ще ги записва
// в подадената структура от тип, който имплементира `Write`;
//
pub fn new(out: W, buffer_size: usize) -> Self {
BufferedLogger {
buffer: Shared::new(Vec::new()),
out: Shared::new(out),
buffer_size: buffer_size,
}
}
// Връща списък от записите, които са буферирани в момента. Записите се очаква да бъдат
// подредени по времето, в което са log-нати, от най-ранни до най-късни.
//
pub fn buffered_entries(&self) -> Vec<String> {
let v: Vec<String> = self.buffer
.borrow()
.iter()
.map(|e| e.text.clone())
.collect();
v
}
}
// Вижте по-долу за бележки за клонирането
impl<W: Write> Clone for BufferedLogger<W> {
fn clone(&self) -> Self {
BufferedLogger {
buffer: self.buffer.clone(),
out: self.out.clone(),
buffer_size: self.buffer_size,
}
}
}
impl<W: Write> Logger for BufferedLogger<W> {
fn push(&mut self, time: Instant, text: &str) {
let entrie = Entrie::new (String::from(text), time);
let pos = self.buffer
.borrow_mut()
.binary_search_by_key(&time , |e: &Entrie| *e.time())
.unwrap_or_else(|e| e);
self.buffer.borrow_mut().insert(pos, entrie);
if self.buffer.borrow().len() >= self.buffer_size {
self.flush();
}
}
fn try_flush(&mut self) -> io::Result<()> {
let entries = self.buffered_entries();
let joined = entries.join("\n");
self.out.borrow_mut().write(joined.as_bytes())?;

Специално написахме в условието (секция "Писане във "файл"), че всеки запис трябва да завършва на \n :). Join-ването не добавя нов ред накрая на последния елемент. Можеше да сложиш едно self.out.borrow_mut().write(b"\n") примерно.

self.buffer.borrow_mut().clear();
Ok(())
}
}
//
// MULTILOGGER
//
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 logger in self.loggers.iter_mut() {
logger.push(time, text);
}
}
fn try_flush(&mut self) -> io::Result<()> {
for logger in self.loggers.iter_mut() {
logger.try_flush()?;
}
Ok(())
}
}
//
// SCOPEDLOGGER
//
pub struct ScopedLogger<L: Logger> {
tag: String,
base: L,
}
impl<L: Logger> ScopedLogger<L> {
pub fn new(tag: &str, base_logger: L) -> Self {
ScopedLogger {
tag: String::from(tag),
base: base_logger,
}
}
}
impl<L: Logger> Logger for ScopedLogger<L> {
fn push(&mut self, time: Instant, text: &str) {
let formatted = format!("[{}] {}", self.tag, text);
self.base.push(time, formatted.as_str());
}
fn try_flush(&mut self) -> io::Result<()> {
self.base.try_flush()
}
}

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

Compiling solution v0.1.0 (/tmp/d20190123-22631-liiqkk/solution)
    Finished dev [unoptimized + debuginfo] target(s) in 4.74s
     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 ... FAILED
test solution_test::test_erroring_io ... ok
test solution_test::test_flushing_the_buffer ... FAILED
test solution_test::test_logger_combinations ... FAILED
test solution_test::test_multilogger_logs_and_flushes_when_needed ... ok
test solution_test::test_multilogger_logs_to_several_ios ... FAILED
test solution_test::test_reordering_logs_in_buffer ... ok
test solution_test::test_reordering_logs_in_io ... FAILED
test solution_test::test_scoped_logger ... FAILED
test solution_test::test_scoped_logger_with_a_string_tag ... FAILED

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\nThree"`,
 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 'assertion failed: `(left == right)`
  left: `"OneTwoThree"`,
 right: `"One\nTwo\nThree\n"`', tests/solution_test.rs:219:5

---- solution_test::test_cloning_a_logger_shares_their_io stdout ----
thread 'solution_test::test_cloning_a_logger_shares_their_io' panicked at 'assertion failed: `(left == right)`
  left: `"First\nSecond\nThird"`,
 right: `"First\nSecond\nThird\n"`', tests/solution_test.rs:175:5

---- solution_test::test_flushing_the_buffer stdout ----
thread 'solution_test::test_flushing_the_buffer' panicked at 'assertion failed: `(left == right)`
  left: `"Some warning\nSome other warning"`,
 right: `"Some warning\nSome other warning\n"`', tests/solution_test.rs:95:5

---- solution_test::test_logger_combinations stdout ----
thread 'solution_test::test_logger_combinations' panicked at 'assertion failed: `(left == right)`
  left: `"[Base1] [Multi] Test entry[Base2] [Multi] Test entry"`,
 right: `"[Base1] [Multi] Test entry\n[Base2] [Multi] Test entry\n"`', tests/solution_test.rs:268:5

---- solution_test::test_multilogger_logs_to_several_ios stdout ----
thread 'solution_test::test_multilogger_logs_to_several_ios' panicked at 'assertion failed: `(left == right)`
  left: `"One\nTwo"`,
 right: `"One\nTwo\n"`', tests/solution_test.rs:241:5

---- solution_test::test_reordering_logs_in_io stdout ----
thread 'solution_test::test_reordering_logs_in_io' panicked at 'assertion failed: `(left == right)`
  left: `"First\nSecond\nThird\nFourth"`,
 right: `"First\nSecond\nThird\nFourth\n"`', tests/solution_test.rs:134:5

---- solution_test::test_scoped_logger stdout ----
thread 'solution_test::test_scoped_logger' panicked at 'assertion failed: `(left == right)`
  left: `"[First] One\n[Second] Two\n[Second] Three\n[First] Four"`,
 right: `"[First] One\n[Second] Two\n[Second] Three\n[First] Four\n"`', tests/solution_test.rs:315:5

---- solution_test::test_scoped_logger_with_a_string_tag stdout ----
thread 'solution_test::test_scoped_logger_with_a_string_tag' panicked at 'assertion failed: `(left == right)`
  left: `"[First] Test"`,
 right: `"[First] Test\n"`', tests/solution_test.rs:329:5


failures:
    solution_test::test_automatic_flushing_when_buffer_limit_is_reached
    solution_test::test_automatic_flushing_when_zero_buffer_limit
    solution_test::test_cloning_a_logger_shares_their_io
    solution_test::test_flushing_the_buffer
    solution_test::test_logger_combinations
    solution_test::test_multilogger_logs_to_several_ios
    solution_test::test_reordering_logs_in_io
    solution_test::test_scoped_logger
    solution_test::test_scoped_logger_with_a_string_tag

test result: FAILED. 6 passed; 9 failed; 0 ignored; 0 measured; 0 filtered out

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

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

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

+use std::time::Instant;
+use std::io;
+use std::rc::Rc;
+use std::cell::{RefCell,Ref, RefMut};
+use std::io::Write;
+
+//
+// LOGGER
+//
+pub trait Logger {
+ // Метод, който добавя нов запис за логване. Първия аргумент, от тип `std::time::Instant`, е
+ // момента във времето, който се асоциира със събитието. Обикновено ще се ползва метода `log`
+ // директно, който запълва този параметър, но метода `push` ще е удобен за тестване на
+ // логиката.
+ //
+ // Втория аргумент е низа, който ще се логва.
+ //
+ fn push(&mut self, time: Instant, text: &str);
+
+ // Метод който ще работи като `push`, с тази разлика, че директно използва `Instant::now()` за
+ // да вземе текущ timestamp.
+ //
+ fn log(&mut self, text: &str) {
+ self.push(Instant::now(), text);
+ }
+
+ // Метод, който записва нещата от вътрешния буфер към някакъв външен носител -- файл, сокет,
+ // стандартния изход. В случай на имплементация, която няма нужда от този метод, винаги може да
+ // се имплементира като просто `Ok(())`.
+ //
+ fn try_flush(&mut self) -> io::Result<()>;
+
+ // Метод, който прави същото като по-горния, но не връща грешка. Вижте по-долу за бележки за
+ // Error handling-а, който очакваме.
+ //
+ fn flush(&mut self) {
+ if let Err(e) = self.try_flush() {
+ eprintln!("{}", e);
+ }
+ }
+}
+
+//
+// SHARED
+//
+struct Shared<T> {
+ v: Rc<RefCell<T>>
+}
+
+impl <T> Shared<T> {
+ fn new(t: T)-> Shared<T> {
+ Shared{v: Rc::new(RefCell::new(t))}
+ }
+}
+
+impl <T> Shared<T> {
+ fn borrow(&self) -> Ref<T> {
+ self.v.borrow()
+ }
+
+ fn borrow_mut(&self) -> RefMut<T> {
+ self.v.borrow_mut()
+ }
+}
+
+impl <T> Clone for Shared<T> {
+ fn clone(&self) -> Self {
+ Shared {
+ v: self.v.clone(),
+ }
+ }
+}
+
+//
+// ENTRIE
+//
+struct Entrie {
+ text: String,
+ time: Instant,
+}
+
+impl Entrie {
+ fn new (text: String, time: Instant) -> Self {
+ Entrie {text, time}
+ }
+
+ fn time (&self) -> &Instant {
+ &self.time
+ }
+}
+
+//
+// BUFFEREDLOGGER
+//
+pub struct BufferedLogger<W: Write> {
+ buffer: Shared<Vec<Entrie>>,
+ out: Shared<W>,
+ buffer_size: usize,
+}
+
+impl<W: Write> BufferedLogger<W> {
+ // Конструира структура, която ще пази записи в буфер с размер `buffer_size`, и ще ги записва
+ // в подадената структура от тип, който имплементира `Write`;
+ //
+ pub fn new(out: W, buffer_size: usize) -> Self {
+ BufferedLogger {
+ buffer: Shared::new(Vec::new()),
+ out: Shared::new(out),
+ buffer_size: buffer_size,
+ }
+ }
+
+ // Връща списък от записите, които са буферирани в момента. Записите се очаква да бъдат
+ // подредени по времето, в което са log-нати, от най-ранни до най-късни.
+ //
+ pub fn buffered_entries(&self) -> Vec<String> {
+ let v: Vec<String> = self.buffer
+ .borrow()
+ .iter()
+ .map(|e| e.text.clone())
+ .collect();
+
+ v
+ }
+}
+
+// Вижте по-долу за бележки за клонирането
+impl<W: Write> Clone for BufferedLogger<W> {
+ fn clone(&self) -> Self {
+ BufferedLogger {
+ buffer: self.buffer.clone(),
+ out: self.out.clone(),
+ buffer_size: self.buffer_size,
+ }
+ }
+}
+
+impl<W: Write> Logger for BufferedLogger<W> {
+ fn push(&mut self, time: Instant, text: &str) {
+ let entrie = Entrie::new (String::from(text), time);
+
+ let pos = self.buffer
+ .borrow_mut()
+ .binary_search_by_key(&time , |e: &Entrie| *e.time())
+ .unwrap_or_else(|e| e);
+
+ self.buffer.borrow_mut().insert(pos, entrie);
+
+ if self.buffer.borrow().len() >= self.buffer_size {
+ self.flush();
+ }
+ }
+
+ fn try_flush(&mut self) -> io::Result<()> {
+ let entries = self.buffered_entries();
+ let joined = entries.join("\n");
+
+ self.out.borrow_mut().write(joined.as_bytes())?;

Специално написахме в условието (секция "Писане във "файл"), че всеки запис трябва да завършва на \n :). Join-ването не добавя нов ред накрая на последния елемент. Можеше да сложиш едно self.out.borrow_mut().write(b"\n") примерно.

+
+ self.buffer.borrow_mut().clear();
+
+ Ok(())
+ }
+}
+
+//
+// MULTILOGGER
+//
+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 logger in self.loggers.iter_mut() {
+ logger.push(time, text);
+ }
+ }
+
+ fn try_flush(&mut self) -> io::Result<()> {
+ for logger in self.loggers.iter_mut() {
+ logger.try_flush()?;
+ }
+
+ Ok(())
+ }
+}
+
+
+//
+// SCOPEDLOGGER
+//
+pub struct ScopedLogger<L: Logger> {
+ tag: String,
+ base: L,
+}
+
+impl<L: Logger> ScopedLogger<L> {
+ pub fn new(tag: &str, base_logger: L) -> Self {
+ ScopedLogger {
+ tag: String::from(tag),
+ base: base_logger,
+ }
+ }
+}
+
+impl<L: Logger> Logger for ScopedLogger<L> {
+ fn push(&mut self, time: Instant, text: &str) {
+ let formatted = format!("[{}] {}", self.tag, text);
+ self.base.push(time, formatted.as_str());
+ }
+
+ fn try_flush(&mut self) -> io::Result<()> {
+ self.base.try_flush()
+ }
+}