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