scanln/lib.rs
1//! The inverse of [`println`]
2//!
3//! [`scanln`] is a macro that reads a line from `stdin`, [removes the trailing
4//! newline][0] (if present), and leaves other whitespace intact. It expands to
5//! an expression that returns a [`String`], so it can be assigned to a variable
6//! or used directly. [`scanln`] may take arguments like those to the [`print`]
7//! macro, which can be used to generate a prompt.
8//!
9//! # Examples
10//!
11//! ## Simple prompt
12//!
13//! ```no_run
14//! # #[macro_use] extern crate scanln;
15//! let input = scanln!("> ");
16//! ```
17//!
18//! This results in a prompt that looks like this (where `_` represents where
19//! the user will start typing):
20//!
21//! ```text
22//! > _
23//! ```
24//!
25//! ## No prompt
26//!
27//! ```no_run
28//! # #[macro_use] extern crate scanln;
29//! let input = scanln!();
30//! ```
31//!
32//! Result:
33//!
34//! ```text
35//! _
36//! ```
37//!
38//! ## Formatted prompt
39//!
40//! ```no_run
41//! # #[macro_use] extern crate scanln;
42//! let input = scanln!("{}", 0);
43//! ```
44//!
45//! Result:
46//!
47//! ```text
48//! 0_
49//! ```
50//!
51//! [0]: https://users.rust-lang.org/t/remove-the-new-line-from-read-line/21380
52
53/// The inverse of [`println`]
54///
55/// Invocations of [`scanln`] expand to an expression retuning a [`String`] that
56/// contains a line of input from `stdin`. It can be invoked with arguments
57/// identical to those of [`print`], and if so, those arguments will be used
58/// to generate a prompt on `stdout`. Input will begin on the same line that the
59/// prompt ends, if any. If no arguments are provided, input will start where
60/// the cursor is, which is likely to be on its own empty line.
61#[macro_export] macro_rules! scanln {
62 // Get the actual input, stripping the trailing '\n' and/or '\r' if present
63 () => {{
64 let mut s = ::std::string::String::new();
65 ::std::io::stdin().read_line(&mut s).unwrap();
66
67 // TODO fix bizarre syntax, blocked on const generics:
68 // https://github.com/rust-lang/rust/issues/39511
69 s.trim_end_matches(&['\n', '\r'][..]).to_owned()
70 }};
71
72 // Print out a prompt, possibly with format arguments
73 ( $($args:tt)* ) => {{
74 print!("{}", format_args!($($args)*));
75
76 // Flush stdout because it's line buffered and we didn't print a newline
77 use ::std::io::Write;
78 ::std::io::stdout().flush().unwrap();
79
80 // Read input
81 $crate::scanln!()
82 }};
83}