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;