Skip to content

Commit b798acf

Browse files
committed
Add input! implementation for the examples
1 parent 72fe2a1 commit b798acf

File tree

3 files changed

+107
-0
lines changed

3 files changed

+107
-0
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ publish = false
1515
[dependencies]
1616

1717
[dev-dependencies]
18+
input = { path = "./crates/input" }
1819
proconio = "=0.3.6"
1920
rand = "0.7.3"

crates/input/Cargo.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[package]
2+
name = "input"
3+
version = "0.1.0"
4+
authors = ["rust-lang-ja Developers"]
5+
edition = "2018"
6+
description = "Provides a `input!` macro for the `examples`."
7+
license = "CC0-1.0"
8+
publish = false
9+
10+
[dependencies]

crates/input/src/lib.rs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
//! A simple `input!` macro with minimal functionality.
2+
//!
3+
//! ```no_run
4+
//! #[macro_use]
5+
//! extern crate input as _;
6+
//!
7+
//! fn main() {
8+
//! input! {
9+
//! a: [u64],
10+
//! }
11+
//! }
12+
//! ```
13+
14+
use std::{
15+
fmt,
16+
io::{self, Read},
17+
str::{FromStr, SplitAsciiWhitespace},
18+
};
19+
20+
#[macro_export]
21+
macro_rules! input {
22+
($($tt:tt)*) => {
23+
let mut __scanner = $crate::Scanner::new().unwrap();
24+
$crate::input_inner!(@scanner(__scanner), @tts($($tt)*));
25+
::std::mem::drop(__scanner);
26+
};
27+
}
28+
29+
#[doc(hidden)]
30+
#[macro_export]
31+
macro_rules! input_inner {
32+
(@scanner($scanner:ident), @tts()) => {};
33+
(@scanner($scanner:ident), @tts(mut $single_tt_pat:tt : $readable:tt)) => {
34+
let mut $single_tt_pat = $crate::read!(from $scanner { $readable });
35+
};
36+
(@scanner($scanner:ident), @tts($single_tt_pat:tt : $readable:tt)) => {
37+
let $single_tt_pat = $crate::read!(from $scanner { $readable });
38+
};
39+
(@scanner($scanner:ident), @tts(mut $single_tt_pat:tt : $readable:tt, $($rest:tt)*)) => {
40+
$crate::input_inner!(@scanner($scanner), @tts(mut $single_tt_pat: $readable));
41+
$crate::input_inner!(@scanner($scanner), @tts($($rest)*));
42+
};
43+
(@scanner($scanner:ident), @tts($single_tt_pat:tt : $readable:tt, $($rest:tt)*)) => {
44+
$crate::input_inner!(@scanner($scanner), @tts($single_tt_pat: $readable));
45+
$crate::input_inner!(@scanner($scanner), @tts($($rest)*));
46+
};
47+
}
48+
49+
#[doc(hidden)]
50+
#[macro_export]
51+
macro_rules! read {
52+
(from $scanner:ident { [$tt:tt] }) => {
53+
$crate::read!(from $scanner { [$tt; $crate::read!(from $scanner { usize })] })
54+
};
55+
(from $scanner:ident { [$tt:tt; $n:expr] }) => {
56+
(0..$n).map(|_| $crate::read!(from $scanner { $tt })).collect::<Vec<_>>()
57+
};
58+
(from $scanner:ident { ($($tt:tt),+) }) => {
59+
($($crate::read!(from $scanner { $tt })),*)
60+
};
61+
(from $scanner:ident { $ty:ty }) => {
62+
$scanner.parse::<$ty>()
63+
};
64+
}
65+
66+
#[doc(hidden)]
67+
pub struct Scanner {
68+
words: SplitAsciiWhitespace<'static>,
69+
}
70+
71+
impl Scanner {
72+
pub fn new() -> io::Result<Self> {
73+
let mut buf = String::with_capacity(1024);
74+
io::stdin().read_to_string(&mut buf)?;
75+
let words = Box::leak(buf.into_boxed_str()).split_ascii_whitespace();
76+
Ok(Self { words })
77+
}
78+
79+
/// Parses the next word.
80+
///
81+
/// # Panics
82+
///
83+
/// Panics if:
84+
///
85+
/// - reached the end of input.
86+
/// - the word is not successfully parsed.
87+
pub fn parse<T>(&mut self) -> T
88+
where
89+
T: FromStr,
90+
T::Err: fmt::Display,
91+
{
92+
let word = self.words.next().expect("reached the end of input");
93+
word.parse()
94+
.unwrap_or_else(|e| panic!("could not parse {:?}: {}", word, e))
95+
}
96+
}

0 commit comments

Comments
 (0)