use core::result::Result;
use gdbstub::conn::{Connection, ConnectionExt};
use patina::peripheral::serial::{SerialIO, shared::SharedSerial};
pub(crate) struct SerialConnection<'a, T: SerialIO> {
transport: &'a SharedSerial<T>,
peeked_byte: Option<u8>,
}
impl<'a, T: SerialIO> SerialConnection<'a, T> {
pub fn new(transport: &'a SharedSerial<T>) -> Self {
SerialConnection { transport, peeked_byte: None }
}
}
impl<T: SerialIO> Connection for SerialConnection<'_, T> {
type Error = patina::error::EfiError;
fn write(&mut self, byte: u8) -> Result<(), Self::Error> {
let buff = [byte];
self.transport.write(&buff)?;
Ok(())
}
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
impl<T: SerialIO> ConnectionExt for SerialConnection<'_, T> {
fn read(&mut self) -> Result<u8, Self::Error> {
if let Some(byte) = self.peeked_byte {
self.peeked_byte = None;
return Ok(byte);
}
self.transport.read()
}
fn peek(&mut self) -> Result<Option<u8>, Self::Error> {
if self.peeked_byte.is_some() {
return Ok(self.peeked_byte);
}
match self.transport.try_read()? {
Some(byte) => {
self.peeked_byte = Some(byte);
Ok(Some(byte))
}
None => Ok(None),
}
}
}
pub struct LoggingSuspender {
level: log::LevelFilter,
}
impl LoggingSuspender {
pub fn suspend() -> Self {
let level = log::max_level();
log::set_max_level(log::LevelFilter::Off);
LoggingSuspender { level }
}
}
impl Drop for LoggingSuspender {
fn drop(&mut self) {
log::set_max_level(self.level);
}
}
#[cfg(test)]
#[cfg_attr(coverage, coverage(off))]
mod tests {
use super::*;
use mockall::mock;
mock! {
Serial {}
impl SerialIO for Serial {
fn init(&mut self);
fn write(&mut self, buffer: &[u8]);
fn read(&mut self) -> u8;
fn try_read(&mut self) -> Option<u8>;
}
}
#[test]
fn test_connection_write() {
let mut mock = MockSerial::new();
mock.expect_write().with(mockall::predicate::eq([0x01])).times(1).returning(|_| ());
mock.expect_write().with(mockall::predicate::eq([0x02])).times(1).returning(|_| ());
mock.expect_write().with(mockall::predicate::eq([0x03])).times(1).returning(|_| ());
let shared = SharedSerial::new(mock);
let mut connection = SerialConnection::new(&shared);
for &byte in &[0x01, 0x02, 0x03] {
let result = connection.write(byte);
assert!(result.is_ok());
}
}
#[test]
fn test_connection_flush() {
let mock = MockSerial::new();
let shared = SharedSerial::new(mock);
let mut connection = SerialConnection::new(&shared);
let result = connection.flush();
assert!(result.is_ok());
}
#[test]
fn test_connection_read() {
let mut mock = MockSerial::new();
mock.expect_read().times(1).returning(|| 0xAA);
mock.expect_read().times(1).returning(|| 0xBB);
mock.expect_read().times(1).returning(|| 0xCC);
let shared = SharedSerial::new(mock);
let mut connection = SerialConnection::new(&shared);
for expected_byte in [0xAA, 0xBB, 0xCC] {
let result = connection.read();
assert!(result.is_ok());
assert_eq!(result.unwrap(), expected_byte);
}
}
#[test]
fn test_connection_peek() {
let mut mock = MockSerial::new();
mock.expect_try_read().times(1).returning(|| Some(0xDD));
mock.expect_try_read().times(1).returning(|| Some(0xEE));
mock.expect_try_read().times(1).returning(|| None);
let shared = SharedSerial::new(mock);
let mut connection = SerialConnection::new(&shared);
let result = connection.peek();
assert!(result.is_ok());
assert_eq!(result.unwrap(), Some(0xDD));
let result = connection.peek();
assert!(result.is_ok());
assert_eq!(result.unwrap(), Some(0xDD));
let result = connection.read();
assert!(result.is_ok());
assert_eq!(result.unwrap(), 0xDD);
let result = connection.peek();
assert!(result.is_ok());
assert_eq!(result.unwrap(), Some(0xEE));
let result = connection.read();
assert!(result.is_ok());
assert_eq!(result.unwrap(), 0xEE);
let result = connection.peek();
assert!(result.is_ok());
assert_eq!(result.unwrap(), None);
}
#[test]
fn test_logging_suspender() {
let original_level = log::max_level();
{
let _suspender = LoggingSuspender::suspend();
assert_eq!(log::max_level(), log::LevelFilter::Off);
}
assert_eq!(log::max_level(), original_level);
}
#[test]
fn test_logging_suspender_nested() {
let original_level = log::max_level();
{
let _suspender1 = LoggingSuspender::suspend();
assert_eq!(log::max_level(), log::LevelFilter::Off);
{
let _suspender2 = LoggingSuspender::suspend();
assert_eq!(log::max_level(), log::LevelFilter::Off);
}
assert_eq!(log::max_level(), log::LevelFilter::Off);
}
assert_eq!(log::max_level(), original_level);
}
}