1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#![recursion_limit = "128"]

extern crate aoc_runner_internal;
extern crate core;
extern crate proc_macro;
extern crate proc_macro2;
extern crate quote;
extern crate syn;

mod generator;
mod map;
mod out;
mod runner;
mod types;
mod utils;

use crate::map::Map;
use crate::utils::is_rls;
use proc_macro as pm;
use proc_macro2 as pm2;
use quote::quote;

thread_local! {
    static AOC_RUNNER: Map = Map::new();
}

#[proc_macro_attribute]
/// # Solution meta
///
/// Use this to flag a function as a solution for a given day :
/// `#[aoc(day1, part1)]`
///
/// You can also add a custom name to the function :
/// `#[aoc(day1, part1, Bytes)]`, it's useful to have multiple solutions to a given day & part and compare them !
///
/// The function must take a single parameter : a `&str` or a `&[u8]`, unless you use a [generator]
/// and return any type implementing `Display`.
///
/// ## Results & Options
///
/// Since 0.2.0, you can output `Result` & `Option` from solution function, with the following constraints :
///  - the output type must be named `Result` or `Option`, `type CustomResult<T> = Result<T, CustomError>;` cannot be used in return position.
///  - the first generic parameter must implement `Display`
///  - for `Result`s, the error must implement `Into<std::error::Error>`
///
/// You still can use a path before the `Result`/`Option`, like this : `std::io::Result<i32>`
///
/// [generator]: attr.aoc_generator.html
pub fn aoc(args: pm::TokenStream, input: pm::TokenStream) -> pm::TokenStream {
    if is_rls() {
        let input: pm2::TokenStream = input.into();
        return pm::TokenStream::from(quote! {
            #[allow(unused)]
            #input
        });
    }

    runner::runner_impl(args, input)
}

#[proc_macro_attribute]
/// # Generator meta
///
/// Use a generator when you need to pre-process your input :
///
/// ## Usage
/// Generator meta have 3 forms :
///  - a generator for the whole day : `#[aoc_generator(day1)]`
///  - a generator for a single part : `#[aoc_generator(day1, part1)]`
///  - a generator for a single (named) solution: `#[aoc_generator(day1, part1, Bytes)]`
///
/// The function must take a single parameter : a `&str` or a `&[u8]`, and output any sized type.
///
/// The corresponding solutions now take any parameter for which `Borrow` is implemented.
///
/// ## Results & Options
///
/// Since 0.2.0, you can output `Result` & `Option` from generator function, with the following constraints :
///  - the output type must be named `Result` or `Option`, `type CustomResult<T> = Result<T, CustomError>;` cannot be used in return position.
///  - for `Result`s, the error must implement `Into<std::error::Error>`
///
/// You still can use a path before the `Result`/`Option`, like this : `std::io::Result<i32>`
///
/// ## Note
/// A generator must be declared before it's solutions.
///
pub fn aoc_generator(args: pm::TokenStream, input: pm::TokenStream) -> pm::TokenStream {
    if is_rls() {
        let input: pm2::TokenStream = input.into();
        return pm::TokenStream::from(quote! {
            #[allow(unused)]
            #input
        });
    }

    generator::generator_impl(args, input)
}

#[proc_macro]
/// # Library declaration
///
/// This macro must be at the end of lib.rs
///
/// ## Usage
/// `aoc_lib! { year = 2018 }`
pub fn aoc_lib(input: pm::TokenStream) -> pm::TokenStream {
    if is_rls() {
        return pm::TokenStream::new();
    }

    out::lib_impl(input)
}

#[proc_macro]
/// # Main declaration
///
/// This macro must be at the end of main.rs
///
/// ## Usage
/// `aoc_main` has 2 forms :
///  - as a standalone binary : `aoc_main! { year = 2018 }`
///  - as a link to a library : `aoc_main! { lib = advent_of_code_2018 }` (you must had `extern crate advent_of_code_2018;` before)
pub fn aoc_main(input: pm::TokenStream) -> pm::TokenStream {
    if is_rls() {
        return pm::TokenStream::from(quote! { fn main() {} });
    }

    out::main_impl(input)
}