Решение на Logging от Николай Данаилов
Към профила на Николай Данаилов
Резултати
- 19 точки от тестове
- 0 бонус точки
- 19 точки общо
- 14 успешни тест(а)
- 1 неуспешни тест(а)
Код
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20190123-22631-fq8mye/solution) Finished dev [unoptimized + debuginfo] target(s) in 5.09s 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 ... 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_zero_buffer_limit stdout ---- thread 'solution_test::test_automatic_flushing_when_zero_buffer_limit' panicked at 'assertion failed: `(left == right)` left: `1`, right: `0`', tests/solution_test.rs:210:9 note: Run with `RUST_BACKTRACE=1` for a backtrace. failures: solution_test::test_automatic_flushing_when_zero_buffer_limit test result: FAILED. 14 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out error: test failed, to rerun pass '--test solution_test'
История (2 версии и 4 коментара)
Николай качи решение на 17.12.2018 12:11 (преди почти 7 години)
use std::time::Instant;
use std::io;
use std::io::Write;
use std::rc::Rc;
use std::cell::RefCell;
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);
}
-#[derive(Default)]
pub struct BufferedLogger<W: Write> {
buffer: Rc<RefCell<Vec<(Instant, String)>>>,
capacity: usize,
out: Rc<RefCell<W>>
}
impl<W: Write> BufferedLogger<W> {
- /// Конструира структура, която ще пази записи в буфер с размер `buffer_size`, и ще ги записва
- /// в подадената структура от тип, който имплементира `Write`;
- ///
pub fn new(out: W, buffer_size: usize) -> Self {
BufferedLogger {
out: Rc::new(RefCell::new(out)),
capacity: buffer_size,
buffer: Rc::new(RefCell::new(vec![]))
}
}
- /// Връща списък от записите, които са буферирани в момента. Записите се очаква да бъдат
- /// подредени по времето, в което са log-нати, от най-ранни до най-късни.
- ///
pub fn buffered_entries(&self) -> Vec<String> {
// Can't figure out how to copy the buffer without
// iterating through it manually
let mut res = vec![];
- for (time, text) in self.buffer.borrow_mut().iter() {
+ for (_time, text) in self.buffer.borrow_mut().iter() {
res.push(text.clone());
}
res
Ако викнеш self.buffer.clone()
ще се клонира rc-то, което не е каквото искаш. Но (*self.buffer).borrow().clone()
ще излезе от rc-то, ще викне borrow()
, за да вземе Ref
от refcell-а, и ще извика clone
на вектора, в крайна сметка. А тъй като викането на методи прави deref coercion, просто това би трябвало да сработи: self.buffer.borrow().clone()
.
Идеята стана. Само дето аз ползвам tuple-и, така че ще трябва да стане с
self.buffer.borrow().iter().map(|(_, t)| t.clone()).collect()
Мерси!
}
}
-/// Вижте по-долу за бележки за клонирането
impl<W: Write> Clone for BufferedLogger<W> {
fn clone(&self) -> Self {
BufferedLogger {
out: self.out.clone(),
buffer: self.buffer.clone(),
capacity: self.capacity.clone()
Типа usize
е Copy
, така че дори и само self.capacity
щеше да свърши работа. Но да кажем, че експлицитния clone може би има някаква симетрия :).
}
}
}
impl<W: Write> Logger for BufferedLogger<W> {
- /// Подходящи имплементации на Logger методите
fn push(&mut self, time: Instant, text: &str) {
self.buffer.borrow_mut().push((time, text.to_string()));
self.buffer.borrow_mut().sort_unstable();
if self.buffer.borrow().len() == self.capacity {
self.flush();
}
Ако buffer_size
е 0 (както е в един от тестовете), това няма да flush-не. Със >=
щеше да сработи :).
}
fn try_flush(&mut self) -> io::Result<()> {
- for (time, string) in self.buffer.borrow().iter() {
- // Create a new string and add a new line to it
- // before writing it
- let mut string = string.clone();
- string.push('\n');
-
- self.out.borrow_mut().write(string.as_bytes());
+ for (_time, string) in self.buffer.borrow().iter() {
+ let string = format!("{}\n", string);
+ self.out.borrow_mut().write(string.as_bytes())?;
}
self.buffer.borrow_mut().clear();
Ok(())
}
fn flush(&mut self) {
match self.try_flush() {
Ok(_) => (),
Err(e) => eprintln!("{}", e)
}
}
}
pub struct MultiLogger {
loggers: Vec<Box<dyn Logger>>
}
impl MultiLogger {
pub fn new() -> Self {
MultiLogger {
loggers: vec![]
}
}
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 &mut self.loggers {
logger.push(time, text);
}
}
fn try_flush(&mut self) -> io::Result<()> {
for logger in &mut self.loggers {
logger.try_flush()?;
}
Ok(())
}
fn flush(&mut self) {
for logger in &mut self.loggers {
logger.flush();
}
}
}
pub struct ScopedLogger<L: Logger> {
tag: String,
logger: L
}
impl<L: Logger> ScopedLogger<L> {
pub fn new(tag: &str, base_logger: L) -> Self {
ScopedLogger {
tag: tag.to_string(),
logger: base_logger
}
}
}
impl<L: Logger> Logger for ScopedLogger<L> {
fn push(&mut self, time: Instant, text: &str) {
let string = format!("[{}] {}", self.tag, text);
self.logger.push(time, &string);
}
fn try_flush(&mut self) -> io::Result<()> {
self.logger.try_flush()
}
fn flush(&mut self) {
self.logger.flush();
}
}
Ако викнеш
self.buffer.clone()
ще се клонира rc-то, което не е каквото искаш. Но(*self.buffer).borrow().clone()
ще излезе от rc-то, ще викнеborrow()
, за да вземеRef
от refcell-а, и ще извикаclone
на вектора, в крайна сметка. А тъй като викането на методи прави deref coercion, просто това би трябвало да сработи:self.buffer.borrow().clone()
.Идеята стана. Само дето аз ползвам tuple-и, така че ще трябва да стане с
Мерси!
Типа
usize
еCopy
, така че дори и самоself.capacity
щеше да свърши работа. Но да кажем, че експлицитния clone може би има някаква симетрия :).Ако
buffer_size
е 0 (както е в един от тестовете), това няма да flush-не. Със>=
щеше да сработи :).