use log::{Level, Log, Metadata, Record};
use std::format_args;

pub struct AsciiSanitizingLogger {
    inner: Box<dyn Log>,
}
impl AsciiSanitizingLogger {
    pub fn new(inner: Box<dyn Log>) -> Self {
        Self { inner }
    }

    fn sanitize(&self, s: &str) -> String {
        s.chars()
            .map(|c| {
                if c.is_ascii_graphic() || c == ' ' {
                    c
                } else {
                    '?'
                }
            })
            .collect()
    }
}
impl Log for AsciiSanitizingLogger {
    fn enabled(&self, metadata: &Metadata) -> bool {
        self.inner.enabled(metadata)
    }
    fn log(&self, record: &Record) {
        if !self.enabled(record.metadata()) {
            return;
        }

        // Format the complete log message first, then sanitize it
        let formatted_message = record.args().to_string();
        let sanitized_message = self.sanitize(&formatted_message);

        let target = record.target().to_string();
        let module_path = record.module_path().map(str::to_string);
        let file = record.file().map(str::to_string);

        // Create a new record with sanitized content.
        // All borrowed data for the new record must have the same lifetime. Since the
        // sanitized message is owned by this function, we must also create owned
        // copies of the other metadata from the original record.
        let sanitized_record = Record::builder()
            .args(format_args!("{}", sanitized_message))
            .level(record.level())
            .target(&target)
            .module_path(module_path.as_deref())
            .file(file.as_deref())
            .line(record.line())
            .build();

        self.inner.log(&sanitized_record);
    }
    fn flush(&self) {
        self.inner.flush();
    }
}
