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
//! Crate for easy input parsing and prompting.
//!
//! This crate allows for easy input constraints, making it simpler to handle user input
//! with specific type constraints and validations.
//!
//! # Example
//!
//! ```
//! use constrained_inputs::input;
//!
//! fn main() {
//! let int = input::<i32>().expect("Input was invalid");
//! println!("Your input integer: {}", int);
//! }
//! ```
use lazy_static::lazy_static;
use std::io;
mod constraints;
pub use constraints::*;
// Just a method to avoid creating multiple stdin objects
lazy_static! {
static ref IO_IN: io::Stdin = io::stdin();
}
/// Error gotten either when input is invalid or some I/O error occurs
#[derive(Debug)]
pub enum InputError<T>
where
T: std::str::FromStr,
{
IoError(io::Error),
ParseError(T::Err),
BreaksConstraint(ConstraintError),
}
/// This function is able to take in an input with a type constraint.
///
/// # Example
///
/// ```
/// use constrained_inputs::input;
///
/// fn main() {
/// let int = input::<i32>().expect("Input was invalid");
/// println!("Your input integer: {}", int);
/// }
/// ```
///
/// # Errors
///
/// This function returns an `InputError` if the input is invalid, if a parsing error occurs,
/// or if the input does not meet the specified constraints.
pub fn input<T>() -> Result<T, InputError<T>>
where
T: std::str::FromStr,
{
input_stream(IO_IN.lock())
}
/// Reads an input from a `BufRead` reader.
///
/// # Errors
///
/// This function returns an `InputError` if the input is invalid, if a parsing error occurs,
/// or if the input does not meet the specified constraints.
pub fn input_stream<T, R>(mut reader: R) -> Result<T, InputError<T>>
where
R: io::BufRead,
T: std::str::FromStr,
{
let mut buf = String::new();
reader
.read_line(&mut buf)
.map_err(|io_err| InputError::IoError(io_err))?;
let input = buf.trim();
input
.parse::<T>()
.map_err(|parse_err| InputError::ParseError(parse_err))
}
/// Prompts user input in the terminal with an added type constraint.
///
/// # WARNING
/// Make sure that the constraint provided is of the same type as the one you expect.
///
/// # Example
///
/// ```
/// use constrained_inputs::{constrained_input, NumberConstraint};
///
/// fn main() {
/// let constraint = NumberConstraint{
/// max: Some(20),
/// min: Some(10),
/// };
/// let int = constrained_input::<i32, _>(constraint).expect("Input was invalid or out of range");
/// println!("Your constrained input integer: {}", int);
/// }
/// ```
///
/// # Errors
///
/// This function returns an `InputError` if the input is invalid, if a parsing error occurs,
/// or if the input does not meet the specified constraints.
pub fn constrained_input<T, C>(constraint: C) -> Result<T, InputError<T>>
where
T: std::str::FromStr,
C: Constraint<T>,
{
let input = input()?;
match constraint.validate(&input) {
ConstraintResult::Valid => Ok(input),
ConstraintResult::Err(err) => Err(InputError::BreaksConstraint(err)),
}
}
#[cfg(test)]
mod test;