bddap_aoc/
lib.rs

1mod api;
2mod challenge;
3mod input_getter;
4mod persist;
5
6use clap::Parser;
7use linkme::distributed_slice;
8
9pub use bddap_aoc_macros::{register, unregistered_challenge};
10pub use challenge::Challenge;
11pub use input_getter::Getter;
12pub use linkme;
13
14#[distributed_slice]
15pub static CHALLENGES: [Challenge] = [..];
16
17#[derive(clap::Parser)]
18#[command(author, version, about, long_about = None)]
19enum Args {
20    #[clap(about = "Run challenge[s]")]
21    Run(Run),
22    #[clap(about = "Save your session token so challenges inputs can be downloaded")]
23    Login,
24}
25
26#[derive(clap::Parser)]
27struct Run {
28    /// Only run challenges for the given year[s]
29    #[arg(short, long)]
30    year: Vec<usize>,
31
32    /// Only run challenges for the given day[s].
33    #[arg(short, long)]
34    day: Vec<usize>,
35
36    /// Only run challenges for the given part[s].
37    #[arg(short, long)]
38    part: Vec<usize>,
39}
40
41/// Get settings from command line arguments.
42pub fn run_default() {
43    let argss = Args::parse();
44
45    let run = match argss {
46        Args::Run(run) => run,
47        Args::Login => {
48            unwrap_or_print(persist::login());
49            return;
50        }
51    };
52
53    let getter = unwrap_or_print(Getter::load());
54
55    let mut todos: Vec<&Challenge> = CHALLENGES
56        .iter()
57        .filter(|c| run.year.contains(&c.year) || run.year.is_empty())
58        .filter(|c| run.day.contains(&c.day) || run.day.is_empty())
59        .filter(|c| run.part.contains(&c.part) || run.part.is_empty())
60        .collect();
61
62    if todos.is_empty() {
63        eprintln!("No matching challenges.");
64    }
65
66    todos.sort_by_key(|c| (c.year, c.day, c.part));
67
68    for challenge in todos {
69        println!(
70            "year {} day {} part {} - {}",
71            challenge.year, challenge.day, challenge.part, challenge.name
72        );
73        let input = match getter.get_input(challenge.year, challenge.day) {
74            Ok(input) => input,
75            Err(api::Error::ChallengeNotReady { time_till_ready }) => {
76                eprintln!(
77                    "Challenge not ready yet. Try again in {}",
78                    humantime::format_duration(time_till_ready)
79                );
80                continue;
81            }
82            Err(api::Error::Other(e)) => {
83                eprintln!("Error getting input: {}", e);
84                continue;
85            }
86        };
87        let output = challenge.run(&input);
88        println!("{}", output);
89        println!();
90    }
91}
92
93fn unwrap_or_print<T, E: std::fmt::Display>(result: Result<T, E>) -> T {
94    match result {
95        Ok(t) => t,
96        Err(e) => {
97            eprintln!("{}", e);
98            std::process::exit(1);
99        }
100    }
101}