use std::fmt::Display;
use anyhow::Context;
use clap::{Arg, ArgMatches, Command};
use crate::{config, Client, Result};
struct Cli<'a, F, R>
where
F: Fn(&str) -> R,
R: Display,
{
event: &'a str,
day: u8,
level: u8,
code: F,
}
impl<'a, F, R> Cli<'a, F, R>
where
F: Fn(&str) -> R,
R: Display,
{
fn new(event: &'a str, day: u8, level: u8, code: F) -> Self {
Self {
event,
day,
level,
code,
}
}
fn run(&self) -> Result<()> {
let cli = Command::new(format!(
"Advent of Code {} - Day {} part {}",
self.event, self.day, self.level
))
.subcommand(Command::new("submit").about("Submit the solution"))
.subcommand(Command::new("open").about("Open the day's page in the browser"))
.subcommand(new_config_subcommand())
.get_matches();
let Some(subcommand) = cli.subcommand() else {
return self.default();
};
match subcommand {
("submit", _) => self.submit(),
("open", _) => self.open(),
(CONFIG_SUBCOMMAND, args) => run_config_subcommand(args),
_ => self.default(),
}
}
fn default(&self) -> Result<()> {
let client = Client::new(self.event, config::session_token()?)?;
let input = client.get_input(self.day)?;
let result = (self.code)(&input);
println!("Result: '{}'", result);
Ok(())
}
fn submit(&self) -> Result<()> {
let client = Client::new(self.event, config::session_token()?)?;
let input = client.get_input(self.day)?;
let result = (self.code)(&input).to_string();
println!(
"Submitting '{}' for AoC {} day {} part {}\n",
result, self.event, self.day, self.level
);
let response = client.submit_solution(self.day, self.level, &result)?;
println!("{}", response);
Ok(())
}
fn open(&self) -> Result<()> {
let url = format!("https://adventofcode.com/{}/day/{}", self.event, self.day);
::open::that(&url).with_context(|| format!("Failed to open '{}'", url))?;
Ok(())
}
}
pub const CONFIG_SUBCOMMAND: &str = "config";
pub fn new_config_subcommand() -> Command {
Command::new(CONFIG_SUBCOMMAND)
.about("Configure advent of code settings")
.arg(
Arg::new("session")
.short('s')
.long("session")
.help("Set the session token / cookie")
.value_name("TOKEN")
.num_args(1),
)
}
pub fn run_config_subcommand(args: &ArgMatches) -> Result<()> {
if let Some(token) = args.get_one::<String>("session") {
config::set_session_token(token)?;
}
Ok(())
}
pub fn run<F, R>(event: &str, day: u8, level: u8, code: F)
where
F: Fn(&str) -> R,
R: Display,
{
if let Err(error) = Cli::new(event, day, level, code).run() {
println!("Error: {:?}", error);
}
}