opentitanlib/io/console/
logged.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5use std::io::Write;
6use std::sync::Mutex;
7use std::task::{Context, Poll, ready};
8use std::time::SystemTime;
9
10use anyhow::Result;
11
12use super::ConsoleDevice;
13use crate::io::middleware::{ConsoleMiddleware, Middleware, UartMiddleware};
14use crate::io::uart::Uart;
15
16/// Middleware that logs the received data.
17pub struct Logged<T> {
18    inner: T,
19    /// If the next character is the beginning of a line.
20    newline: Mutex<bool>,
21}
22
23impl<T> Logged<T> {
24    pub fn new(inner: T) -> Logged<T> {
25        Self {
26            inner,
27            newline: Mutex::new(true),
28        }
29    }
30}
31
32impl<T: ConsoleDevice> Middleware for Logged<T> {
33    type Inner = T;
34    fn inner(&self) -> &T {
35        &self.inner
36    }
37}
38
39impl<T: ConsoleDevice> ConsoleMiddleware for Logged<T> {
40    fn poll_read_impl(&self, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<Result<usize>> {
41        // Establish order between readers, if there are many.
42        let mut newline = self.newline.lock().unwrap();
43        let len = ready!(self.inner.poll_read(cx, buf))?;
44
45        let mut stdout = std::io::stdout().lock();
46        for ch in buf[..len].iter().copied() {
47            if *newline {
48                let t = humantime::format_rfc3339_millis(SystemTime::now());
49                stdout.write_fmt(format_args!("[{}  console]", t))?;
50            }
51
52            *newline = ch == b'\n';
53
54            stdout.write_all(std::slice::from_ref(&ch))?;
55        }
56        stdout.flush()?;
57
58        Poll::Ready(Ok(len))
59    }
60}
61
62impl<T: Uart> UartMiddleware for Logged<T> {
63    fn clear_rx_buffer_impl(&self) -> Result<()> {
64        *self.newline.lock().unwrap() = true;
65        self.inner.clear_rx_buffer()
66    }
67}