Решение на Logging от Милен Дончев

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

Към профила на Милен Дончев

Резултати

  • 4 точки от тестове
  • 0 бонус точки
  • 4 точки общо
  • 3 успешни тест(а)
  • 12 неуспешни тест(а)

Код

use std::time::Instant;
use std::io;
use std::io::Write;
use std::rc::Rc;
use std::cmp::Ordering;
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);
/// Метод, който записва нещата от вътрешния буфер към някакъв външен носител -- файл, сокет,
/// стандартния изход. В случай на имплементация, която няма нужда от този метод, винаги може да
/// се имплементира като просто `Ok(())`.
///
fn try_flush(&mut self) -> io::Result<()>;
/// Метод, който прави същото като по-горния, но не връща грешка. Вижте по-долу за бележки за
/// Error handling-а, който очакваме.
///
fn flush(&mut self);
}
#[derive(Eq)]
pub struct Wrapper {
text: String,
time: Instant
}
impl Ord for Wrapper {
fn cmp(&self, other: &Wrapper) -> Ordering {
self.time.cmp(&other.time)
}
}
impl PartialOrd for Wrapper {
fn partial_cmp(&self, other: &Wrapper) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for Wrapper {
fn eq(&self, other: &Wrapper) -> bool {
self.time == other.time
}
}
pub struct BufferedLogger<W: Write> {
out: Rc<W>, buffer_size: usize, data: Vec<Wrapper>
}
impl<W: Write> BufferedLogger<W> {
/// Конструира структура, която ще пази записи в буфер с размер `buffer_size`, и ще ги записва
/// в подадената структура от тип, който имплементира `Write`;
///
pub fn new(out: W, buffer_size: usize) -> Self {
BufferedLogger{ out: Rc::new(out), buffer_size: buffer_size, data: Vec::new()}
}
/// Връща списък от записите, които са буферирани в момента. Записите се очаква да бъдат
/// подредени по времето, в което са log-нати, от най-ранни до най-късни.
///
pub fn buffered_entries(&self) -> Vec<String> {
let mut copy = self.clone();
copy.data.sort();
let result = copy.data.iter().map(|ref x| (&x.text).to_string() ).collect::<Vec<_>>();
result
}
}
impl<W: Write> Clone for BufferedLogger<W> {
fn clone(&self) -> Self {
let mut res_clone:Vec<Wrapper> = Vec::new();
let out_clone = Rc::clone(&self.out);
for item in self.data.iter() {
let q = Wrapper { time: item.time, text: item.text.to_string()};
res_clone.push(q);
}
BufferedLogger{ out: out_clone, buffer_size: self.buffer_size, data: res_clone }

Мне. Да копираш данните от досегашния вектор в клонирания не ти помага със споделянето на данни оттук нататък. Rc<RefCell<Vec<Wrapper>>> щеше да ти свърши работа.

}
}
impl<W: Write> Logger for BufferedLogger<W> {
/// Подходящи имплементации на Logger методите
fn push(&mut self, time: Instant, text: &str){
let item = Wrapper{ time: time, text: text.to_string()};
self.data.push(item);
}
/// Метод който ще работи като `push`, с тази разлика, че директно използва `Instant::now()` за
/// да вземе текущ timestamp.
///
fn log(&mut self, text: &str) {
let item = Wrapper{ time: Instant::now(), text: text.to_string()};
self.data.push(item);
}
/// Метод, който записва нещата от вътрешния буфер към някакъв външен носител -- файл, сокет,
/// стандартния изход. В случай на имплементация, която няма нужда от този метод, винаги може да
/// се имплементира като просто `Ok(())`.
///
fn try_flush(&mut self) -> io::Result<()>{
let entries = self.buffered_entries();
for item in entries.iter() {
let q = item.as_bytes();
let mut out_clone = Rc::clone(&self.out);
let x = Rc::get_mut(&mut out_clone).unwrap();
let q = x.write_all(q);
match q {
Result::Ok(val) => val,
Result::Err(err) => eprintln!("{}", err),
}
let q = x.write_all("\n".as_bytes());
match q {
Result::Ok(val) => val,
Result::Err(err) => eprintln!("{}", err),
}
}
Ok(())
}

Метода try_flush връща io::Result. В него ако имаш грешка, я връщаш. Във flush я печаташ на екрана, не тук.

Отвъд това, не съм сигурен защо ползваш get_mut и даже Rc::clone тук. Заслужава си да огледаш останалите решения и документацията на Rc и RefCell методите.

/// Метод, който прави същото като по-горния, но не връща грешка. Вижте по-долу за бележки за
/// Error handling-а, който очакваме.
///
fn flush(&mut self){
let entries = self.buffered_entries();
for item in entries.iter() {
let q = item.as_bytes();
let mut out_clone = Rc::clone(&self.out);
let x = Rc::get_mut(&mut out_clone).unwrap();
let q = x.write_all(q);
q.unwrap();
let q = x.write_all("\n".as_bytes());
q.unwrap();
}
}
}
pub struct MultiLogger {
/// Каквито полета решите, че ви трябват
vector: Vec<Box<Logger>>
}
impl MultiLogger {
pub fn new() -> Self {
MultiLogger{vector: Vec::new()}
}
pub fn log_to<L: Logger + 'static>(&mut self, logger: L) {
self.vector.push(Box::new(logger));
}
}
impl Logger for MultiLogger {
/// Подходящи имплементации на Logger методите
fn push(&mut self, _time: Instant, _text: &str) {
// for x in self.vector.iter() {
// x.push(time, text);
// }
unimplemented!()
}
/// Метод който ще работи като `push`, с тази разлика, че директно използва `Instant::now()` за
/// да вземе текущ timestamp.
///
fn log(&mut self, _text: &str) {
// for x in self.vector.iter() {
// x.log(text);
// }
unimplemented!()
}
/// Метод, който записва нещата от вътрешния буфер към някакъв външен носител -- файл, сокет,
/// стандартния изход. В случай на имплементация, която няма нужда от този метод, винаги може да
/// се имплементира като просто `Ok(())`.
///
fn try_flush(&mut self) -> io::Result<()>{
// for x in self.vector.iter() {
// x.try_flush();
// }
Ok(())
}
/// Метод, който прави същото като по-горния, но не връща грешка. Вижте по-долу за бележки за
/// Error handling-а, който очакваме.
///
fn flush(&mut self) {
// for x in self.vector.iter() {
// x.flush();
// }
unimplemented!()
}
}
pub struct ScopedLogger<L: Logger> {
_data: Vec<Rc<L>>
}
impl<L: Logger> ScopedLogger<L> {
pub fn new(_tag: &str, _base_logger: L) -> Self {
unimplemented!()
}
}
impl<L: Logger> Logger for ScopedLogger<L> {
/// Подходящи имплементации на Logger методите
fn push(&mut self, _time: Instant, _text: &str){
unimplemented!()
}
/// Метод който ще работи като `push`, с тази разлика, че директно използва `Instant::now()` за
/// да вземе текущ timestamp.
///
fn log(&mut self, _text: &str) {
unimplemented!()
}
/// Метод, който записва нещата от вътрешния буфер към някакъв външен носител -- файл, сокет,
/// стандартния изход. В случай на имплементация, която няма нужда от този метод, винаги може да
/// се имплементира като просто `Ok(())`.
///
fn try_flush(&mut self) -> io::Result<()>{
Ok(())
}
/// Метод, който прави същото като по-горния, но не връща грешка. Вижте по-долу за бележки за
/// Error handling-а, който очакваме.
///
fn flush(&mut self){
unimplemented!()
}
}

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

Compiling solution v0.1.0 (/tmp/d20190123-22631-19t0ony/solution)
    Finished dev [unoptimized + debuginfo] target(s) in 5.05s
     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 ... FAILED
test solution_test::test_cloning_a_logger_shares_their_io ... FAILED
test solution_test::test_erroring_io ... FAILED
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 ... FAILED
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: `3`,
 right: `0`', tests/solution_test.rs:194:9
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: `1`,
 right: `0`', tests/solution_test.rs:210:9

---- solution_test::test_cloning_a_logger_shares_a_buffer stdout ----
thread 'solution_test::test_cloning_a_logger_shares_a_buffer' panicked at 'assertion failed: `(left == right)`
  left: `["Second"]`,
 right: `["First", "Second", "Third"]`', tests/solution_test.rs:154:5

---- solution_test::test_cloning_a_logger_shares_their_io stdout ----
thread 'solution_test::test_cloning_a_logger_shares_their_io' panicked at 'called `Option::unwrap()` on a `None` value', libcore/option.rs:355:21

---- solution_test::test_erroring_io stdout ----
thread 'solution_test::test_erroring_io' panicked at 'called `Option::unwrap()` on a `None` value', libcore/option.rs:355:21

---- solution_test::test_flushing_the_buffer stdout ----
thread 'solution_test::test_flushing_the_buffer' panicked at 'called `Option::unwrap()` on a `None` value', libcore/option.rs:355:21

---- solution_test::test_logger_combinations stdout ----
thread 'solution_test::test_logger_combinations' panicked at 'not yet implemented', src/lib.rs:215:9

---- solution_test::test_multilogger_logs_and_flushes_when_needed stdout ----
thread 'solution_test::test_multilogger_logs_and_flushes_when_needed' panicked at 'not yet implemented', src/lib.rs:174:9

---- solution_test::test_multilogger_logs_to_several_ios stdout ----
thread 'solution_test::test_multilogger_logs_to_several_ios' panicked at 'not yet implemented', src/lib.rs:174:9

---- solution_test::test_reordering_logs_in_io stdout ----
thread 'solution_test::test_reordering_logs_in_io' panicked at 'called `Option::unwrap()` on a `None` value', libcore/option.rs:355:21

---- solution_test::test_scoped_logger stdout ----
thread 'solution_test::test_scoped_logger' panicked at 'not yet implemented', src/lib.rs:215:9

---- solution_test::test_scoped_logger_with_a_string_tag stdout ----
thread 'solution_test::test_scoped_logger_with_a_string_tag' panicked at 'not yet implemented', src/lib.rs:215:9


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_a_buffer
    solution_test::test_cloning_a_logger_shares_their_io
    solution_test::test_erroring_io
    solution_test::test_flushing_the_buffer
    solution_test::test_logger_combinations
    solution_test::test_multilogger_logs_and_flushes_when_needed
    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. 3 passed; 12 failed; 0 ignored; 0 measured; 0 filtered out

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

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

Милен обнови решението на 18.12.2018 16:34 (преди почти 2 години)

+use std::time::Instant;
+use std::io;
+use std::io::Write;
+use std::rc::Rc;
+use std::cmp::Ordering;
+
+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);
+
+ /// Метод, който записва нещата от вътрешния буфер към някакъв външен носител -- файл, сокет,
+ /// стандартния изход. В случай на имплементация, която няма нужда от този метод, винаги може да
+ /// се имплементира като просто `Ok(())`.
+ ///
+ fn try_flush(&mut self) -> io::Result<()>;
+
+ /// Метод, който прави същото като по-горния, но не връща грешка. Вижте по-долу за бележки за
+ /// Error handling-а, който очакваме.
+ ///
+ fn flush(&mut self);
+}
+
+#[derive(Eq)]
+pub struct Wrapper {
+ text: String,
+ time: Instant
+}
+
+impl Ord for Wrapper {
+ fn cmp(&self, other: &Wrapper) -> Ordering {
+ self.time.cmp(&other.time)
+ }
+}
+
+impl PartialOrd for Wrapper {
+ fn partial_cmp(&self, other: &Wrapper) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl PartialEq for Wrapper {
+ fn eq(&self, other: &Wrapper) -> bool {
+ self.time == other.time
+ }
+}
+
+pub struct BufferedLogger<W: Write> {
+ out: Rc<W>, buffer_size: usize, data: Vec<Wrapper>
+}
+
+impl<W: Write> BufferedLogger<W> {
+ /// Конструира структура, която ще пази записи в буфер с размер `buffer_size`, и ще ги записва
+ /// в подадената структура от тип, който имплементира `Write`;
+ ///
+ pub fn new(out: W, buffer_size: usize) -> Self {
+ BufferedLogger{ out: Rc::new(out), buffer_size: buffer_size, data: Vec::new()}
+ }
+
+ /// Връща списък от записите, които са буферирани в момента. Записите се очаква да бъдат
+ /// подредени по времето, в което са log-нати, от най-ранни до най-късни.
+ ///
+ pub fn buffered_entries(&self) -> Vec<String> {
+ let mut copy = self.clone();
+ copy.data.sort();
+ let result = copy.data.iter().map(|ref x| (&x.text).to_string() ).collect::<Vec<_>>();
+ result
+ }
+}
+
+impl<W: Write> Clone for BufferedLogger<W> {
+ fn clone(&self) -> Self {
+ let mut res_clone:Vec<Wrapper> = Vec::new();
+ let out_clone = Rc::clone(&self.out);
+ for item in self.data.iter() {
+ let q = Wrapper { time: item.time, text: item.text.to_string()};
+ res_clone.push(q);
+ }
+ BufferedLogger{ out: out_clone, buffer_size: self.buffer_size, data: res_clone }

Мне. Да копираш данните от досегашния вектор в клонирания не ти помага със споделянето на данни оттук нататък. Rc<RefCell<Vec<Wrapper>>> щеше да ти свърши работа.

+ }
+}
+
+impl<W: Write> Logger for BufferedLogger<W> {
+ /// Подходящи имплементации на Logger методите
+ fn push(&mut self, time: Instant, text: &str){
+ let item = Wrapper{ time: time, text: text.to_string()};
+ self.data.push(item);
+ }
+
+ /// Метод който ще работи като `push`, с тази разлика, че директно използва `Instant::now()` за
+ /// да вземе текущ timestamp.
+ ///
+ fn log(&mut self, text: &str) {
+ let item = Wrapper{ time: Instant::now(), text: text.to_string()};
+ self.data.push(item);
+ }
+
+ /// Метод, който записва нещата от вътрешния буфер към някакъв външен носител -- файл, сокет,
+ /// стандартния изход. В случай на имплементация, която няма нужда от този метод, винаги може да
+ /// се имплементира като просто `Ok(())`.
+ ///
+ fn try_flush(&mut self) -> io::Result<()>{
+ let entries = self.buffered_entries();
+ for item in entries.iter() {
+ let q = item.as_bytes();
+ let mut out_clone = Rc::clone(&self.out);
+ let x = Rc::get_mut(&mut out_clone).unwrap();
+
+ let q = x.write_all(q);
+ match q {
+ Result::Ok(val) => val,
+ Result::Err(err) => eprintln!("{}", err),
+ }
+
+ let q = x.write_all("\n".as_bytes());
+ match q {
+ Result::Ok(val) => val,
+ Result::Err(err) => eprintln!("{}", err),
+ }
+ }
+ Ok(())
+ }

Метода try_flush връща io::Result. В него ако имаш грешка, я връщаш. Във flush я печаташ на екрана, не тук.

Отвъд това, не съм сигурен защо ползваш get_mut и даже Rc::clone тук. Заслужава си да огледаш останалите решения и документацията на Rc и RefCell методите.

+
+ /// Метод, който прави същото като по-горния, но не връща грешка. Вижте по-долу за бележки за
+ /// Error handling-а, който очакваме.
+ ///
+ fn flush(&mut self){
+ let entries = self.buffered_entries();
+ for item in entries.iter() {
+ let q = item.as_bytes();
+ let mut out_clone = Rc::clone(&self.out);
+ let x = Rc::get_mut(&mut out_clone).unwrap();
+
+ let q = x.write_all(q);
+ q.unwrap();
+
+ let q = x.write_all("\n".as_bytes());
+ q.unwrap();
+ }
+ }
+}
+
+pub struct MultiLogger {
+ /// Каквито полета решите, че ви трябват
+ vector: Vec<Box<Logger>>
+}
+
+impl MultiLogger {
+ pub fn new() -> Self {
+ MultiLogger{vector: Vec::new()}
+ }
+
+ pub fn log_to<L: Logger + 'static>(&mut self, logger: L) {
+ self.vector.push(Box::new(logger));
+ }
+}
+
+impl Logger for MultiLogger {
+ /// Подходящи имплементации на Logger методите
+ fn push(&mut self, _time: Instant, _text: &str) {
+ // for x in self.vector.iter() {
+ // x.push(time, text);
+ // }
+ unimplemented!()
+ }
+
+ /// Метод който ще работи като `push`, с тази разлика, че директно използва `Instant::now()` за
+ /// да вземе текущ timestamp.
+ ///
+ fn log(&mut self, _text: &str) {
+ // for x in self.vector.iter() {
+ // x.log(text);
+ // }
+ unimplemented!()
+ }
+
+ /// Метод, който записва нещата от вътрешния буфер към някакъв външен носител -- файл, сокет,
+ /// стандартния изход. В случай на имплементация, която няма нужда от този метод, винаги може да
+ /// се имплементира като просто `Ok(())`.
+ ///
+ fn try_flush(&mut self) -> io::Result<()>{
+ // for x in self.vector.iter() {
+ // x.try_flush();
+ // }
+ Ok(())
+ }
+
+ /// Метод, който прави същото като по-горния, но не връща грешка. Вижте по-долу за бележки за
+ /// Error handling-а, който очакваме.
+ ///
+ fn flush(&mut self) {
+ // for x in self.vector.iter() {
+ // x.flush();
+ // }
+ unimplemented!()
+ }
+}
+
+pub struct ScopedLogger<L: Logger> {
+ _data: Vec<Rc<L>>
+}
+
+impl<L: Logger> ScopedLogger<L> {
+ pub fn new(_tag: &str, _base_logger: L) -> Self {
+ unimplemented!()
+ }
+}
+
+impl<L: Logger> Logger for ScopedLogger<L> {
+ /// Подходящи имплементации на Logger методите
+ fn push(&mut self, _time: Instant, _text: &str){
+ unimplemented!()
+ }
+
+ /// Метод който ще работи като `push`, с тази разлика, че директно използва `Instant::now()` за
+ /// да вземе текущ timestamp.
+ ///
+ fn log(&mut self, _text: &str) {
+ unimplemented!()
+ }
+
+ /// Метод, който записва нещата от вътрешния буфер към някакъв външен носител -- файл, сокет,
+ /// стандартния изход. В случай на имплементация, която няма нужда от този метод, винаги може да
+ /// се имплементира като просто `Ok(())`.
+ ///
+ fn try_flush(&mut self) -> io::Result<()>{
+ Ok(())
+ }
+
+ /// Метод, който прави същото като по-горния, но не връща грешка. Вижте по-долу за бележки за
+ /// Error handling-а, който очакваме.
+ ///
+ fn flush(&mut self){
+ unimplemented!()
+ }
+}