1use std::error;
31use std::fmt;
32use std::str::FromStr;
33
34#[non_exhaustive]
35#[derive(Debug, PartialEq)]
36pub enum Error {
37 MissingMatch,
38 MissingClosingBrace,
39 UnexpectedValue(u8, Option<u8>),
40 InvalidUtf8(Vec<u8>),
41 PartialUtf8(usize, Vec<u8>),
42 Parse(String, &'static str),
43}
44
45impl error::Error for Error {}
46
47impl fmt::Display for Error {
48 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
49 use crate::Error::*;
50 use std::str::from_utf8;
51
52 match *self {
53 InvalidUtf8(ref raw) => write!(f, "input was not valid utf8: {:?}", raw),
54 Parse(ref s, arg) => write!(f, "could not parse {} as target type of {}", s, arg),
55 UnexpectedValue(exp, act) => write!(
56 f,
57 "found value {:?} not matching the pattern value {}",
58 act.map(|b| b as char),
59 exp as char
60 ),
61 PartialUtf8(n, ref raw) => write!(
62 f,
63 "input was only partially valid utf8: \"{}\" followed by {:?}",
64 from_utf8(&raw[..n]).unwrap(),
65 &raw[n..]
66 ),
67 MissingMatch => write!(f, "Bad read! format string: did not contain {{}}"),
68 MissingClosingBrace => write!(
69 f,
70 "found single open curly brace at the end of the format string"
71 ),
72 }
73 }
74}
75
76pub fn match_next(expected: u8, iter: &mut dyn Iterator<Item = u8>) -> Result<(), Error> {
77 let next = iter.next();
78 if next != Some(expected) {
79 return Err(Error::UnexpectedValue(expected, next));
80 }
81 Ok(())
82}
83
84pub fn parse_capture<T>(
85 name: &'static str,
86 next: Option<u8>,
87 iter: &mut dyn Iterator<Item = u8>,
88) -> Result<T, Error>
89where
90 T: FromStr,
91 <T as FromStr>::Err: ::std::fmt::Debug,
92{
93 static WHITESPACES: &[u8] = b"\t\r\n ";
94 let raw: Vec<u8> = match next {
95 Some(c) => iter.take_while(|&ch| ch != c).collect(),
96 None => iter
97 .skip_while(|ch| WHITESPACES.contains(ch))
98 .take_while(|ch| !WHITESPACES.contains(ch))
99 .collect(),
100 };
101 match String::from_utf8(raw) {
102 Ok(s) => FromStr::from_str(&s).map_err(|_| Error::Parse(s, name)),
103 Err(e) => {
104 let n = e.utf8_error().valid_up_to();
105 let raw = e.into_bytes();
106 if n == 0 {
107 Err(Error::InvalidUtf8(raw))
108 } else {
109 Err(Error::PartialUtf8(n, raw))
110 }
111 }
112 }
113}
114
115#[macro_export]
133macro_rules! try_read(
134 () => { $crate::try_read!("{}") };
135 ($text:expr) => {{
136 (|| -> std::result::Result<_, $crate::Error> {
137 use std::io::Write;
138 std::io::stdout().flush().unwrap();
139 let __try_read_var__;
140 $crate::try_scan!($text, __try_read_var__);
141 Ok(__try_read_var__)
142 })()
143 }};
144 ($text:expr, $input:expr) => {{
145 (|| -> std::result::Result<_, $crate::Error> {
146 use std::io::Write;
147 std::io::stdout().flush().unwrap();
148 let __try_read_var__;
149 $crate::try_scan!($input => $text, __try_read_var__);
150 Ok(__try_read_var__)
151 })()
152 }};
153);
154
155#[macro_export]
169macro_rules! try_scan(
170 ($pattern:expr, $($arg:expr),*) => {
171 use ::std::io::Read;
172 $crate::try_scan!(::std::io::stdin().bytes().map(std::result::Result::unwrap) => $pattern, $($arg),*);
173 };
174 ($input:expr => $pattern:expr, $($arg:expr),*) => {{
175 $crate::try_scan!(@impl question_mark; $input => $pattern, $($arg),*)
176 }};
177 (@question_mark: $($e:tt)+) => {{
179 ($($e)+)?
180 }};
181 (@unwrap: $($e:tt)+) => {{
183 ($($e)+).unwrap()
184 }};
185 (@impl $action:tt; $input:expr => $pattern:expr, $($arg:expr),*) => {{
186 #![allow(clippy::try_err)]
187 use $crate::{Error, match_next, parse_capture};
188
189 let pattern: &'static str = $pattern;
191 let stdin: &mut dyn Iterator<Item = u8> = &mut ($input);
192
193 let mut pattern = pattern.bytes();
194
195 $(
196 $arg = loop {
197 match $crate::try_scan!(@$action: pattern.next().ok_or(Error::MissingMatch)) {
198 b'{' => match $crate::try_scan!(@$action: pattern.next().ok_or(Error::MissingClosingBrace)) {
199 b'{' => $crate::try_scan!(@$action: match_next(b'{', stdin)),
200 b'}' => break $crate::try_scan!(@$action: parse_capture(stringify!($arg), pattern.next(), stdin)),
201 _ => return $crate::try_scan!(@$action: Err(Error::MissingClosingBrace)),
202 },
203 c => $crate::try_scan!(@$action: match_next(c, stdin)),
204 }
205 };
206 )*
207
208 for c in pattern {
209 $crate::try_scan!(@$action: match_next(c, stdin))
210 }
211
212 format_args!($pattern, $({let _ = &$arg; ""}),*);
213 }};
214);
215
216#[macro_export]
218macro_rules! read(
219 ($($arg:tt)*) => {
220 $crate::try_read!($($arg)*).unwrap()
221 };
222);
223
224#[macro_export]
226macro_rules! scan(
227 ($text:expr, $($arg:expr),*) => {
228 {
229 use ::std::io::Read;
230 $crate::scan!(::std::io::stdin().bytes().map(std::result::Result::unwrap) => $text, $($arg),*);
231 }
232 };
233 ($input:expr => $pattern:expr, $($arg:expr),*) => {{
234 $crate::try_scan!(@impl unwrap; $input => $pattern, $($arg),*)
235 }};
236);
237
238#[cfg(test)]
239mod tests {
240 use std::convert::Infallible;
241
242 use super::*;
243
244 #[derive(Debug)]
246 struct NoDisplay;
247 impl FromStr for NoDisplay {
248 type Err = Infallible;
249 fn from_str(_: &str) -> Result<Self, Self::Err> {
250 Ok(Self)
251 }
252 }
253 #[test]
254 fn test_no_display() {
255 let _: NoDisplay = read!("{}", "".bytes());
256 }
257}