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
11pub trait FromStdin: Sized {
13 type Error: std::error::Error;
14
15 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 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}