1use core::future::Future;
12
13pub trait CliInput {
20 type Error: core::fmt::Debug;
21
22 fn read_line<'io, 'buf>(
27 &'io mut self,
28 buf: &'buf mut [u8],
29 ) -> impl Future<Output = Result<Option<&'buf str>, Self::Error>> + 'io
30 where
31 'buf: 'io;
32}
33
34pub trait CliOutput {
36 type Error: core::fmt::Debug;
37
38 fn write_line(&mut self, line: &str) -> impl Future<Output = Result<(), Self::Error>>;
39
40 fn flush(&mut self) -> impl Future<Output = Result<(), Self::Error>>;
41}
42
43#[cfg(feature = "tokio-stdio")]
44pub use stdio::{StdioInput, StdioOutput, stdio_split};
45
46#[cfg(feature = "tokio-stdio")]
47mod stdio {
48 use super::{CliInput, CliOutput};
49 use std::io;
50 use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader, Stdin, Stdout};
51
52 pub struct StdioInput {
54 reader: BufReader<Stdin>,
55 }
56
57 pub struct StdioOutput {
59 writer: Stdout,
60 }
61
62 pub fn stdio_split() -> (StdioInput, StdioOutput) {
64 (
65 StdioInput {
66 reader: BufReader::new(tokio::io::stdin()),
67 },
68 StdioOutput {
69 writer: tokio::io::stdout(),
70 },
71 )
72 }
73
74 impl CliInput for StdioInput {
75 type Error = io::Error;
76
77 async fn read_line<'io, 'buf>(
78 &'io mut self,
79 buf: &'buf mut [u8],
80 ) -> Result<Option<&'buf str>, Self::Error>
81 where
82 'buf: 'io,
83 {
84 let mut line = String::new();
89 let n = self.reader.read_line(&mut line).await?;
90 if n == 0 && line.is_empty() {
91 return Ok(None);
92 }
93 let trimmed_len = {
94 let s = line.as_str();
95 let mut end = s.len();
96 if s.ends_with('\n') {
97 end -= 1;
98 if s[..end].ends_with('\r') {
99 end -= 1;
100 }
101 }
102 end
103 };
104 let bytes = &line.as_bytes()[..trimmed_len];
105 if bytes.len() > buf.len() {
106 return Err(io::Error::new(
107 io::ErrorKind::InvalidData,
108 "input line exceeds buffer size",
109 ));
110 }
111 buf[..bytes.len()].copy_from_slice(bytes);
112 let line_len = bytes.len();
113 let out = core::str::from_utf8(&buf[..line_len]).expect("utf8 in = utf8 out");
114 Ok(Some(out))
115 }
116 }
117
118 impl CliOutput for StdioOutput {
119 type Error = io::Error;
120
121 async fn write_line(&mut self, line: &str) -> Result<(), Self::Error> {
122 self.writer.write_all(line.as_bytes()).await?;
123 self.writer.write_all(b"\n").await?;
124 Ok(())
125 }
126
127 async fn flush(&mut self) -> Result<(), Self::Error> {
128 self.writer.flush().await
129 }
130 }
131}