proconio_derive/
lib.rs

1// Copyright 2019 statiolake <statiolake@gmail.com>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be copied, modified, or
6// distributed except according to those terms.
7
8#![recursion_limit = "128"]
9#![allow(clippy::needless_doctest_main)]
10
11//! Macros to easily derive `Readable` and make stdout faster.
12//!
13//! proconio_derive provides two procedural macros (attributes): `derive_readable` and `fastout`.
14//!
15//! # Examples for `#[derive_readable]`
16//!
17//! ```
18//! # extern crate proconio;
19//! # extern crate proconio_derive;
20//! use proconio::input;
21//! # use proconio::source::auto::AutoSource;
22//! use proconio_derive::derive_readable;
23//!
24//! // Unit struct can derive readable.  This generates a no-op for the reading.  Not ignoring
25//! // the read value, but simply skip reading process.  You cannot use it to discard the input.
26//! #[derive_readable]
27//! #[derive(PartialEq, Debug)]
28//! struct Weight;
29//!
30//! #[derive_readable]
31//! #[derive(PartialEq, Debug)]
32//! struct Cost(i32);
33//!
34//! #[derive_readable]
35//! #[derive(Debug)]
36//! struct Edge {
37//!     from: usize,
38//!     to: proconio::marker::Usize1, // The real Edge::to has type usize.
39//!     weight: Weight,
40//!     cost: Cost,
41//! }
42//!
43//! fn main() {
44//! #   let source = AutoSource::from("12 32 35");
45//!     input! {
46//! #       from source,
47//!         edge: Edge,
48//!     }
49//!
50//!     // if you enter "12 32 35" to the stdin, the values are as follows.
51//!     assert_eq!(edge.from, 12);
52//!     assert_eq!(edge.to, 31);
53//!     assert_eq!(edge.weight, Weight);
54//!     assert_eq!(edge.cost, Cost(35));
55//! }
56//! ```
57//!
58//! # Examples for `#[fastout]`
59//!
60//! ```
61//! use proconio_derive::fastout;
62//!
63//! #[fastout]
64//! fn main() {
65//!     print!("{}{}, ", 'h', "ello"); // "hello"       (no newline)
66//!     println!("{}!", "world");      // "world!\n"
67//!     println!("{}", 123456789);     // "123456789\n"
68//! }
69//! ```
70extern crate proc_macro;
71
72use proc_macro::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree};
73use proc_macro2::{Span as Span2, TokenStream as TokenStream2};
74use quote::ToTokens;
75use syn::parse::Parse;
76use syn::Stmt;
77
78mod derive_readable;
79mod fastout;
80
81/// Derives `Readable` for your own type.
82///
83/// If every member of your struct implements `Readable`, your own type can also be `Readable`.
84/// All you have to do is just add `#[derive_readable]` to your type definition.  This macro
85/// automatically implements `Readable` to your struct and translate your struct's member type to
86/// the output type of the read.  For example, if you have `Usize1` in your struct, it will
87/// actually be defined as `usize`.  Of course the `Usize1`'s  `Readable` implementation is used to
88/// read.
89#[proc_macro_attribute]
90pub fn derive_readable(attr: TokenStream, input: TokenStream) -> TokenStream {
91    derive_readable::main(attr, input)
92}
93
94/// Enables buffering for stdout.
95///
96/// You cannot create a closure containing `print!` or `println!` in `#[fastout]` function.  This
97/// is because the closure cannot implement `Send` since `StdoutLock`, which is not a `Send`, is
98/// internally captured into the closure.  This causes a trait bound mismatch when used with
99/// function requiring its argument closure to be a `Send`, such as `std::thread::spawn()`.
100///
101/// ```compile_fail
102/// use proconio::fastout;
103///
104/// use std::thread;
105///
106/// #[fastout]
107/// fn main() {
108///    thread::Builder::new()
109///        .stack_size(32 * 1024 * 1024)
110///        .spawn(|| {
111///            println!("Hi!");
112///        })
113///        .unwrap()
114///        .join()
115///        .unwrap();
116/// }
117/// ```
118///
119/// It is too conservative to make all of such closures compilation error because it is actually no
120/// problem to use such a closure only inside a single thread.  However, since trait bound check is
121/// done after macro expansions, there is no way to check whther the closure is required to be a
122/// `Send` or not.  And the compiler error message for actual mismatch of a `Send` requirement is
123/// too confusing, pointing out codes you didn't write (macro-expanded codes) as an error position.
124/// In conclusion, for user-friendliness, all of them are prohibited for now.
125///
126/// Internally this is the same with
127///
128/// ```
129/// let __proconio_stdout = ::std::io::stdout();
130/// let mut __proconio_stdout = ::std::io::BufWriter::new(__proconio_stdout.lock());
131///
132/// #[allow(unused_macros)]
133/// macro_rules! print {
134///     ($($tt:tt)*) => {{
135///         use std::io::Write as _;
136///         ::std::write!(__proconio_stdout, $($tt)*).unwrap();
137///     }};
138/// }
139///
140/// #[allow(unused_macros)]
141/// macro_rules! println {
142///     ($($tt:tt)*) => {{
143///         use std::io::Write as _;
144///         ::std::writeln!(__proconio_stdout, $($tt)*).unwrap();
145///     }};
146/// }
147///
148/// let __proconio_res = {
149///     // Your code goes here
150/// };
151/// <::std::io::BufWriter<::std::io::StdoutLock> as ::std::io::Write>::flush(
152///     &mut __proconio_stdout
153/// ).unwrap();
154/// return __proconio_res;
155/// ```
156#[proc_macro_attribute]
157pub fn fastout(attr: TokenStream, input: TokenStream) -> TokenStream {
158    fastout::main(attr, input)
159}
160
161fn compile_error_at(args: TokenStream2, start: Span2, end: Span2) -> Stmt {
162    let start = start.unwrap();
163    let end = end.unwrap();
164
165    let group = TokenStream::from(args)
166        .into_iter()
167        .map(|x| set_span(x, Span::call_site()))
168        .collect();
169
170    let mut r = Vec::<TokenTree>::new();
171    r.push(set_span(Ident::new("compile_error", start), start));
172    r.push(set_span(Punct::new('!', Spacing::Alone), start));
173    r.push(set_span(Group::new(Delimiter::Parenthesis, group), end));
174    r.push(set_span(Punct::new(';', Spacing::Alone), end));
175
176    syn::parse(r.into_iter().collect()).expect(concat!(
177        "Failed to parse auto-generated compile_error! macro.  ",
178        "This is a bug in `proconio`.  ",
179        "Please report this issue from ",
180        "<https://github.com/statiolake/proconio-rs/issues>."
181    ))
182}
183
184fn set_span<T: Into<TokenTree>>(token: T, span: Span) -> TokenTree {
185    let mut token = token.into();
186    token.set_span(span);
187    token
188}
189
190fn get_span_range(tokens: TokenStream) -> (Span, Span) {
191    let mut tokens = tokens.into_iter();
192
193    let start = match tokens.next() {
194        Some(start) => start.span(),
195        None => return (Span::call_site(), Span::call_site()),
196    };
197    let end = tokens.fold(start, |_, item| item.span());
198
199    (start, end)
200}
201
202fn set_span_range<T: ToTokens + Parse>(tokens: T, start: Span, end: Span) -> T {
203    let tokens = TokenStream::from(tokens.into_token_stream()).into_iter();
204
205    let mut first = true;
206    let tokens = tokens.map(|mut token| {
207        if first {
208            token.set_span(start);
209            first = false;
210        } else {
211            token.set_span(end);
212        }
213
214        token
215    });
216
217    syn::parse(tokens.collect()).expect(concat!(
218        "Failed to parse respanned token stream.  ",
219        "This is a bug in `proconio`.  ",
220        "Please report this issue from ",
221        "<https://github.com/statiolake/proconio-rs/issues>."
222    ))
223}