constrained_inputs/lib.rs
1//! Crate for easy input parsing and prompting.
2//!
3//! This crate allows for easy input constraints, making it simpler to handle user input
4//! with specific type constraints and validations.
5//!
6//! # Example
7//!
8//! ```
9//! use constrained_inputs::input;
10//!
11//! fn main() {
12//! let int = input::<i32>().expect("Input was invalid");
13//! println!("Your input integer: {}", int);
14//! }
15//! ```
16
17use lazy_static::lazy_static;
18use std::io;
19
20mod constraints;
21pub use constraints::*;
22
23// Just a method to avoid creating multiple stdin objects
24lazy_static! {
25 static ref IO_IN: io::Stdin = io::stdin();
26}
27
28/// Error gotten either when input is invalid or some I/O error occurs
29#[derive(Debug)]
30pub enum InputError<T>
31where
32 T: std::str::FromStr,
33{
34 IoError(io::Error),
35 ParseError(T::Err),
36 BreaksConstraint(ConstraintError),
37}
38
39/// This function is able to take in an input with a type constraint.
40///
41/// # Example
42///
43/// ```
44/// use constrained_inputs::input;
45///
46/// fn main() {
47/// let int = input::<i32>().expect("Input was invalid");
48/// println!("Your input integer: {}", int);
49/// }
50/// ```
51///
52/// # Errors
53///
54/// This function returns an `InputError` if the input is invalid, if a parsing error occurs,
55/// or if the input does not meet the specified constraints.
56pub fn input<T>() -> Result<T, InputError<T>>
57where
58 T: std::str::FromStr,
59{
60 input_stream(IO_IN.lock())
61}
62
63/// Reads an input from a `BufRead` reader.
64///
65/// # Errors
66///
67/// This function returns an `InputError` if the input is invalid, if a parsing error occurs,
68/// or if the input does not meet the specified constraints.
69pub fn input_stream<T, R>(mut reader: R) -> Result<T, InputError<T>>
70where
71 R: io::BufRead,
72 T: std::str::FromStr,
73{
74 let mut buf = String::new();
75 reader
76 .read_line(&mut buf)
77 .map_err(|io_err| InputError::IoError(io_err))?;
78 let input = buf.trim();
79 input
80 .parse::<T>()
81 .map_err(|parse_err| InputError::ParseError(parse_err))
82}
83
84/// Prompts user input in the terminal with an added type constraint.
85///
86/// # WARNING
87/// Make sure that the constraint provided is of the same type as the one you expect.
88///
89/// # Example
90///
91/// ```
92/// use constrained_inputs::{constrained_input, NumberConstraint};
93///
94/// fn main() {
95/// let constraint = NumberConstraint{
96/// max: Some(20),
97/// min: Some(10),
98/// };
99/// let int = constrained_input::<i32, _>(constraint).expect("Input was invalid or out of range");
100/// println!("Your constrained input integer: {}", int);
101/// }
102/// ```
103///
104/// # Errors
105///
106/// This function returns an `InputError` if the input is invalid, if a parsing error occurs,
107/// or if the input does not meet the specified constraints.
108pub fn constrained_input<T, C>(constraint: C) -> Result<T, InputError<T>>
109where
110 T: std::str::FromStr,
111 C: Constraint<T>,
112{
113 let input = input()?;
114 match constraint.validate(&input) {
115 ConstraintResult::Valid => Ok(input),
116 ConstraintResult::Err(err) => Err(InputError::BreaksConstraint(err)),
117 }
118}
119
120#[cfg(test)]
121mod test;