Exercício: Trait de Logger

Vamos projetar um utilitário de registro (log) simples, usando um trait Logger com um método log. O código que pode registrar seu progresso pode então receber um &impl Logger. Nos testes, isso pode colocar mensagens no arquivo de log de teste, enquanto em uma compilação de produção, ele enviaria mensagens para um servidor de log.

No entanto, o StderrLogger fornecido abaixo registra todas as mensagens, independentemente da verbosidade. Sua tarefa é escrever um tipo VerbosityFilter que ignorará mensagens acima de uma verbosidade máxima.

Este é um padrão comum: uma struct que envolve uma implementação de trait e implementa esse mesmo trait, adicionando comportamento no processo. Que outros tipos de wrappers podem ser úteis em um utilitário de registro?

use std::fmt::Display;

pub trait Logger {
    /// Registra uma mensagem no nível de verbosidade fornecido.
    fn log(&self, verbosity: u8, message: impl Display);
}

struct StderrLogger;

impl Logger for StderrLogger {
    fn log(&self, verbosity: u8, message: impl Display) {
        eprintln!("verbosidade={verbosity}: {message}");
    }
}

fn do_things(logger: &impl Logger) {
    logger.log(5, "PSC (_FYI_)");
    logger.log(2, "oh-oh");
}

// TODO: Definir e implementar `VerbosityFilter`.

fn main() {
    let l = VerbosityFilter { max_verbosity: 3, inner: StderrLogger };
    do_things(&l);
}