tiny_input/lib.rs
1//! Tiny input macros.
2//!
3//! This crate provides three macros for receiving user input:
4//! [`tiny_input!`], [`input!`] and [`raw_input!`].
5//!
6//! [`raw_input!`] is used for when you just need the string (while handling I/O errors):
7//!
8//! ```no_run
9//! use tiny_input::raw_input;
10//!
11//! let name = raw_input!("What is your name? ").unwrap();
12//!
13//! println!("Hello, {name}!");
14//! ```
15//!
16//! [`tiny_input!`] is useful for when panicking on I/O errors is fine,
17//! and you only need to parse the input:
18//!
19//! ```no_run
20//! use tiny_input::tiny_input;
21//!
22//! let value: u64 = tiny_input!("the square of ").unwrap();
23//!
24//! println!("is {}", value * value);
25//! ```
26//!
27//! [`input!`] is when you need to handle both I/O and parsing errors:
28//!
29//! ```no_run
30//! use tiny_input::{input, Error};
31//!
32//! match input!(as u64, "the square of ") {
33//! Ok(value) => println!("is {}", value * value),
34//! Err(error) => match error {
35//! Error::Fetch(fetch_error) => eprintln!("failed to fetch: {fetch_error}"),
36//! Error::Parse(parse_error) => eprintln!("failed to parse: {parse_error}"),
37//! },
38//! }
39//! ```
40//!
41//! As one might have noticed, there are two kinds of [`tiny_input!`] and [`input!`],
42//! one that attempts to infer the type, and one where you can provide the type explicitly.
43
44#![forbid(unsafe_code)]
45#![forbid(missing_docs)]
46
47use thiserror::Error;
48
49/// Represents errors that can occur when processing inputs.
50#[derive(Debug, Error)]
51#[error(transparent)]
52pub enum Error<E> {
53 /// Fetch error. Returned when any I/O errors occur,
54 /// such as when writing to [`stdout`] and flushing it,
55 /// as well as when reading from [`stdin`].
56 ///
57 /// [`stdin`]: std::io::stdin
58 /// [`stdout`]: std::io::stdout
59 Fetch(std::io::Error),
60 /// Parse error, which is contrained to implement the [`Error`] trait.
61 /// Returned when parsing into `T` fails; the [`T::Err`] is wrapped into this variant.
62 ///
63 /// [`T::Err`]: std::str::FromStr::Err
64 /// [`Error`]: std::error::Error
65 Parse(E),
66}
67
68/// The specialized result type to be used in this library.
69pub type Result<T, E> = std::result::Result<T, Error<E>>;
70
71/// The message used for expecting values.
72pub const FETCH_ERROR: &str = "I/O error occured while fetching input";
73
74/// Invokes [`raw_input!`], panicking on I/O errors before parsing the string.
75#[macro_export]
76macro_rules! tiny_input {
77 (as $type: ty $(, $($token: tt)+)?) => {
78 $crate::raw_input!($($($token)+)?).expect($crate::FETCH_ERROR).parse::<$type>()
79 };
80 ($($token: tt)*) => {
81 $crate::raw_input!($($token)*).expect($crate::FETCH_ERROR).parse()
82 };
83}
84
85/// Similar to [`tiny_input!`], except I/O and parse errors are wrapped into [`enum@Error<E>`].
86#[macro_export]
87macro_rules! input {
88 (as $type: ty $(, $($token: tt)+)?) => {
89 $crate::raw_input!($($($token)+)?)
90 .map_err($crate::Error::Fetch)
91 .and_then(|string| string.parse::<$type>().map_err($crate::Error::Parse))
92 };
93 ($($token: tt)*) => {
94 $crate::raw_input!($($token)*)
95 .map_err($crate::Error::Fetch)
96 .and_then(|string| string.parse().map_err($crate::Error::Parse))
97 };
98}
99
100/// Fetches raw inputs, returning the resulting [`String`] and propagating I/O errors.
101#[macro_export]
102macro_rules! raw_input {
103 ($($token: tt)+) => {{
104 use ::std::io::Write;
105
106 let mut stdout = ::std::io::stdout().lock();
107
108 // avoid using `?` operator here
109
110 match write!(stdout, $($token)+) {
111 // we do not really need to know the byte count
112 Ok(_) => match stdout.flush() {
113 Ok(_) => $crate::raw_input!(),
114 Err(error) => Err(error),
115 },
116 Err(error) => Err(error),
117 }
118 }};
119 () => {{
120 use ::std::io::BufRead;
121
122 let mut string = ::std::string::String::new();
123
124 match ::std::io::stdin().lock().read_line(&mut string) {
125 // we do not need the byte count here
126 Ok(_) => {
127 string.pop(); // remove the newline character, if there is one
128
129 Ok(string)
130 },
131 Err(error) => Err(error),
132 }
133 }};
134}