saneput/
lib.rs

1#![doc = include_str!("../README.md")]
2
3pub use saneput_proc::{input, input_checked};
4
5use std::{
6    num::{ParseFloatError, ParseIntError},
7    string::FromUtf8Error,
8    io::{Read, Stdin},
9};
10
11/// Type that can be read from standrard input.
12pub trait FromStdin: Sized {
13    type Error: std::error::Error;
14
15    /// Reads the value from standrard input with optional radix.
16    fn read_cin(cin: &mut Stdin, radix: Option<u32>) -> Result<Self, Self::Error>;
17}
18
19impl FromStdin for String {
20    type Error = FromUtf8Error;
21
22    fn read_cin(cin: &mut Stdin, radix: Option<u32>) -> Result<Self, Self::Error> {
23        assert_eq!(radix, None, "String does not accept a radix argument");
24
25        String::from_utf8(copy_til_whitespace(cin))
26    }
27}
28
29macro_rules! impl_from_cin_prim {
30    ($($ty:ty),*) => {
31        $(
32            impl FromStdin for $ty {
33                type Error = ParseIntError;
34
35                // Parsing done manually in effort to avoid allocation, not sure if it's the best
36                // way to go about this.
37                fn read_cin(cin: &mut Stdin, radix: Option<u32>) -> Result<Self, Self::Error> {
38                    let buf = $crate::copy_til_whitespace(cin);
39                    <$ty>::from_str_radix(&String::from_utf8_lossy(&buf[..]), radix.unwrap_or(10))
40                }
41            }
42        )*
43    };
44}
45
46macro_rules! impl_from_cin_float {
47    ($($ty:ty),*) => {
48        $(
49            impl FromStdin for $ty {
50                type Error = ParseFloatError;
51
52                fn read_cin(cin: &mut Stdin, radix: Option<u32>) -> Result<Self, Self::Error> {
53                    assert!(radix.is_none(), concat!(stringify!($ty), " does not accept radix argument"));
54
55                    let buf = $crate::copy_til_whitespace(cin);
56                    String::from_utf8_lossy(&buf[..]).parse()
57                }
58            }
59        )*
60    };
61}
62
63impl_from_cin_prim!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize);
64impl_from_cin_float!(f32, f64);
65
66fn copy_til_whitespace(cin: &mut Stdin) -> Vec<u8> {
67    cin.bytes()
68        .flatten()
69        .take_while(|b| !b.is_ascii_whitespace() && !b.is_ascii_control())
70        .collect()
71}