namada_io/
lib.rs

1//! Traits for implementing IO handlers. This is to enable
2//! generic IO. The defaults are the obvious Rust native
3//! functions.
4
5#![allow(clippy::print_stdout, clippy::print_stderr)]
6
7pub mod client;
8
9#[cfg(feature = "async-send")]
10pub use std::marker::Send as MaybeSend;
11#[cfg(feature = "async-send")]
12pub use std::marker::Sync as MaybeSync;
13
14pub use client::Client;
15use namada_core::*;
16
17#[allow(missing_docs)]
18#[cfg(not(feature = "async-send"))]
19pub trait MaybeSync {}
20#[cfg(not(feature = "async-send"))]
21impl<T> MaybeSync for T where T: ?Sized {}
22
23#[allow(missing_docs)]
24#[cfg(not(feature = "async-send"))]
25pub trait MaybeSend {}
26#[cfg(not(feature = "async-send"))]
27impl<T> MaybeSend for T where T: ?Sized {}
28
29/// NOOP progress bar implementation.
30#[derive(Debug, Clone, Copy)]
31pub struct DevNullProgressBar;
32
33/// Mechanism that allows keeping track of the progress
34/// of some operation.
35pub trait ProgressBar {
36    /// Query the amount of virtual elements the progress tracker
37    /// is tracking.
38    fn upper_limit(&self) -> u64;
39
40    /// Set the amount of virtual elements the progress tracker
41    /// is tracking.
42    fn set_upper_limit(&mut self, limit: u64);
43
44    /// Announce that `amount` virtual elements have been
45    /// processed.
46    fn increment_by(&mut self, amount: u64);
47
48    /// Specify a message to be display alongside the bar.
49    fn message(&mut self, message: String);
50
51    /// Signal that a progress bar has completed
52    fn finish(&mut self);
53}
54
55impl ProgressBar for DevNullProgressBar {
56    fn upper_limit(&self) -> u64 {
57        0
58    }
59
60    fn set_upper_limit(&mut self, _: u64) {}
61
62    fn increment_by(&mut self, _: u64) {}
63
64    fn message(&mut self, _: String) {}
65
66    fn finish(&mut self) {}
67}
68
69#[cfg(not(target_family = "wasm"))]
70impl ProgressBar for kdam::Bar {
71    fn upper_limit(&self) -> u64 {
72        self.total as u64
73    }
74
75    fn set_upper_limit(&mut self, limit: u64) {
76        self.total = limit as usize;
77    }
78
79    fn increment_by(&mut self, amount: u64) {
80        kdam::BarExt::update(self, amount as usize).unwrap();
81    }
82
83    fn message(&mut self, message: String) {
84        kdam::BarExt::write(self, message).unwrap();
85    }
86
87    fn finish(&mut self) {
88        println!();
89    }
90}
91
92/// A trait that abstracts out I/O operations
93#[cfg_attr(feature = "async-send", async_trait::async_trait)]
94#[cfg_attr(not(feature = "async-send"), async_trait::async_trait(?Send))]
95pub trait Io {
96    /// Print the given string
97    fn print(&self, output: impl AsRef<str>) {
98        print!("{}", output.as_ref());
99    }
100
101    /// Flush the output
102    fn flush(&self) {
103        use std::io::Write;
104        std::io::stdout().flush().unwrap();
105    }
106
107    /// Print the given string with a newline
108    fn println(&self, output: impl AsRef<str>) {
109        println!("{}", output.as_ref());
110    }
111
112    /// Print the given string into the given Writer
113    fn write<W: std::io::Write>(
114        &self,
115        mut writer: W,
116        output: impl AsRef<str>,
117    ) -> std::io::Result<()> {
118        write!(writer, "{}", output.as_ref())
119    }
120
121    /// Print the given string into the given Writer and terminate with newline
122    fn writeln<W: std::io::Write>(
123        &self,
124        mut writer: W,
125        output: impl AsRef<str>,
126    ) -> std::io::Result<()> {
127        writeln!(writer, "{}", output.as_ref())
128    }
129
130    /// Print the given error string
131    fn eprintln(&self, output: impl AsRef<str>) {
132        eprintln!("{}", output.as_ref());
133    }
134
135    /// Read a string from input
136    async fn read(&self) -> std::io::Result<String> {
137        #[cfg(not(target_family = "wasm"))]
138        {
139            read_aux(tokio::io::stdin()).await
140        }
141        #[cfg(target_family = "wasm")]
142        {
143            unreachable!("Wasm should not perform general IO")
144        }
145    }
146
147    /// Display the given prompt and return the string input
148    async fn prompt(
149        &self,
150        question: impl AsRef<str> + MaybeSync + MaybeSend,
151    ) -> String {
152        #[cfg(not(target_family = "wasm"))]
153        {
154            prompt_aux(
155                tokio::io::stdin(),
156                tokio::io::stdout(),
157                question.as_ref(),
158            )
159            .await
160        }
161        #[cfg(target_family = "wasm")]
162        {
163            unreachable!(
164                "Wasm should not perform general IO; received call for input \
165                 with question\n: {}",
166                question.as_ref()
167            )
168        }
169    }
170}
171
172/// Rust native I/O handling.
173#[derive(Default)]
174pub struct StdIo;
175
176#[cfg_attr(feature = "async-send", async_trait::async_trait)]
177#[cfg_attr(not(feature = "async-send"), async_trait::async_trait(?Send))]
178impl Io for StdIo {}
179
180/// Ignores all I/O operations.
181pub struct NullIo;
182
183#[cfg_attr(feature = "async-send", async_trait::async_trait)]
184#[cfg_attr(not(feature = "async-send"), async_trait::async_trait(?Send))]
185impl Io for NullIo {
186    fn print(&self, _output: impl AsRef<str>) {}
187
188    fn flush(&self) {}
189
190    fn println(&self, _output: impl AsRef<str>) {}
191
192    fn write<W: std::io::Write>(
193        &self,
194        mut _writer: W,
195        _output: impl AsRef<str>,
196    ) -> std::io::Result<()> {
197        Ok(())
198    }
199
200    fn writeln<W: std::io::Write>(
201        &self,
202        mut _writer: W,
203        _output: impl AsRef<str>,
204    ) -> std::io::Result<()> {
205        Ok(())
206    }
207
208    fn eprintln(&self, _output: impl AsRef<str>) {}
209
210    async fn read(&self) -> std::io::Result<String> {
211        panic!("Unsupported operation")
212    }
213
214    async fn prompt(
215        &self,
216        _question: impl AsRef<str> + MaybeSend + MaybeSync,
217    ) -> String {
218        panic!("Unsupported operation")
219    }
220}
221
222/// A generic function for displaying a prompt to users and reading
223/// in their response.
224#[cfg(not(target_family = "wasm"))]
225pub async fn prompt_aux<R, W>(
226    mut reader: R,
227    mut writer: W,
228    question: &str,
229) -> String
230where
231    R: tokio::io::AsyncReadExt + Unpin,
232    W: tokio::io::AsyncWriteExt + Unpin,
233{
234    writer
235        .write_all(question.as_bytes())
236        .await
237        .expect("Unable to write");
238    writer.flush().await.unwrap();
239    let mut s = String::new();
240    reader.read_to_string(&mut s).await.expect("Unable to read");
241    s
242}
243
244/// A generic function for reading input from users
245#[cfg(not(target_family = "wasm"))]
246pub async fn read_aux<R>(mut reader: R) -> tokio::io::Result<String>
247where
248    R: tokio::io::AsyncReadExt + Unpin,
249{
250    let mut s = String::new();
251    reader.read_to_string(&mut s).await?;
252    Ok(s)
253}
254
255/// Convenience macro for formatting arguments to
256/// [`Io::print`]
257#[macro_export]
258macro_rules! display {
259    ($io:expr) => {
260        $io.print("")
261    };
262    ($io:expr, $w:expr; $($args:tt)*) => {
263        $io.write($w, format_args!($($args)*).to_string())
264    };
265    ($io:expr,$($args:tt)*) => {
266        $io.print(format_args!($($args)*).to_string())
267    };
268}
269
270/// Convenience macro for formatting arguments to
271/// [`Io::println`] and [`Io::writeln`]
272#[macro_export]
273macro_rules! display_line {
274    ($io:expr) => {
275        $io.println("")
276    };
277    ($io:expr, $w:expr; $($args:tt)*) => {
278        $io.writeln($w, format_args!($($args)*).to_string())
279    };
280    ($io:expr,$($args:tt)*) => {
281        $io.println(format_args!($($args)*).to_string())
282    };
283}
284
285/// Convenience macro for formatting arguments to
286/// [`Io::eprintln`]
287#[macro_export]
288macro_rules! edisplay_line {
289    ($io:expr,$($args:tt)*) => {
290        $io.eprintln(format_args!($($args)*).to_string())
291    };
292}
293
294#[macro_export]
295/// A convenience macro for formatting the user prompt before
296/// forwarding it to the [`Io::prompt`] method.
297macro_rules! prompt {
298    ($io:expr,$($arg:tt)*) => {{
299        $io.prompt(format!("{}", format_args!($($arg)*)))
300    }}
301}
302
303#[cfg_attr(feature = "async-send", async_trait::async_trait)]
304#[cfg_attr(not(feature = "async-send"), async_trait::async_trait(?Send))]
305pub trait NamadaIo: Sized + MaybeSync + MaybeSend {
306    /// A client with async request dispatcher method
307    type Client: Client + MaybeSend + Sync;
308    /// Captures the input/output streams used by this object
309    type Io: Io + MaybeSend + MaybeSync;
310
311    /// Obtain the client for communicating with the ledger
312    fn client(&self) -> &Self::Client;
313
314    /// Obtain the input/output handle for this context
315    fn io(&self) -> &Self::Io;
316}