Skip to main content

embassy_shell/
io.rs

1use alloc::boxed::Box;
2use core::future::Future;
3use core::pin::Pin;
4
5use embedded_io::Error as _;
6
7use crate::error::{Error, Result};
8
9/// A boxed future used as the return type of command handlers.
10///
11/// Command handlers return this type so that every command shares one
12/// concrete (type-erased) future type, which is what makes it possible to
13/// store arbitrary commands in a single table.
14pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;
15
16/// Dyn-compatible writer abstraction used to type-erase the concrete
17/// transport writer from command handlers.
18trait DynWrite {
19    fn dyn_write_all<'a>(&'a mut self, buf: &'a [u8]) -> BoxFuture<'a, Result<()>>;
20    fn dyn_flush<'a>(&'a mut self) -> BoxFuture<'a, Result<()>>;
21}
22
23impl<W> DynWrite for W
24where
25    W: embedded_io_async::Write + ?Sized,
26{
27    fn dyn_write_all<'a>(&'a mut self, buf: &'a [u8]) -> BoxFuture<'a, Result<()>> {
28        Box::pin(async move {
29            embedded_io_async::Write::write_all(self, buf)
30                .await
31                .map_err(|e| Error::Io(e.kind()))
32        })
33    }
34
35    fn dyn_flush<'a>(&'a mut self) -> BoxFuture<'a, Result<()>> {
36        Box::pin(async move {
37            embedded_io_async::Write::flush(self)
38                .await
39                .map_err(|e| Error::Io(e.kind()))
40        })
41    }
42}
43
44/// Output handle passed to command handlers.
45///
46/// Wraps the shell's writer. The concrete transport type is hidden, so a
47/// command works unchanged over UART, USB CDC, etc.
48///
49/// [`Io`] also implements [`embedded_io_async::Write`], so commands can pass
50/// it to any code expecting that trait (error type [`Error`]).
51pub struct Io<'a> {
52    w: &'a mut dyn DynWrite,
53}
54
55impl<'a> Io<'a> {
56    pub(crate) fn new<W: embedded_io_async::Write>(w: &'a mut W) -> Self {
57        Io { w }
58    }
59
60    /// Write a string slice, flushing so that it reaches the terminal.
61    pub async fn print(&mut self, s: &str) -> Result<()> {
62        self.w.dyn_write_all(s.as_bytes()).await?;
63        self.w.dyn_flush().await
64    }
65
66    /// Write a string slice followed by `\r\n`, flushing the transport.
67    pub async fn println(&mut self, s: &str) -> Result<()> {
68        self.w.dyn_write_all(s.as_bytes()).await?;
69        self.w.dyn_write_all(b"\r\n").await?;
70        self.w.dyn_flush().await
71    }
72
73    /// Write a byte slice, flushing the transport.
74    pub async fn write_all(&mut self, buf: &[u8]) -> Result<()> {
75        self.w.dyn_write_all(buf).await?;
76        self.w.dyn_flush().await
77    }
78
79    /// Flush the underlying writer.
80    pub async fn flush(&mut self) -> Result<()> {
81        self.w.dyn_flush().await
82    }
83}
84
85impl embedded_io::ErrorType for Io<'_> {
86    type Error = Error;
87}
88
89impl embedded_io_async::Write for Io<'_> {
90    async fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Error> {
91        self.w.dyn_write_all(buf).await?;
92        Ok(buf.len())
93    }
94
95    async fn flush(&mut self) -> core::result::Result<(), Error> {
96        self.w.dyn_flush().await
97    }
98}