Skip to content

Commit

Permalink
Scaffold: Custom formatter and start on init
Browse files Browse the repository at this point in the history
  • Loading branch information
connorslade committed Nov 26, 2023
1 parent 963a59f commit 03deb5d
Show file tree
Hide file tree
Showing 8 changed files with 335 additions and 39 deletions.
50 changes: 38 additions & 12 deletions scaffold/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,35 @@ pub struct Args {
pub subcommand: SubCommand,
}

impl Args {
pub fn as_token_args(&self) -> &TokenArgs {
match &self.subcommand {
SubCommand::Token(args) => args,
_ => panic!("Expected token subcommand"),
}
}

pub fn as_timer_args(&self) -> &TimerArgs {
match &self.subcommand {
SubCommand::Timer(args) => args,
_ => panic!("Expected timer subcommand"),
}
}

pub fn as_init_args(&self) -> &InitArgs {
match &self.subcommand {
SubCommand::Init(args) => args,
_ => panic!("Expected init subcommand"),
}
}
}

#[derive(Parser, Debug)]
pub enum SubCommand {
/// Verify that the session token provided is still valid
Verify,
/// Update the token stored in `AOC_TOKEN`.
Token {
/// The session token you grabbed from the website.
token: String,
},
Token(TokenArgs),
/// Waits for the next midnight (EST) from December first to the twenty-fifth then returns.
/// Chaining this command with another command, like init, will ensure that the input is fetched as soon as it is available.
Timer(TimerArgs),
Expand All @@ -33,6 +53,12 @@ pub enum SubCommand {
Init(InitArgs),
}

#[derive(Parser, Debug)]
pub struct TokenArgs {
/// The session token you grabbed from the website.
pub token: String,
}

#[derive(Parser, Debug)]
pub struct TimerArgs {
/// Time in seconds to offset the timer by.
Expand All @@ -50,16 +76,16 @@ pub struct TimerArgs {
#[derive(Parser, Debug)]
pub struct InitArgs {
/// A formatter that will be used to get the path for the input file.
#[arg(short, long, default_value = "{year}/{day:pad}.txt")]
input_location: String,
#[arg(short, long, default_value = "{{year}}/{{day:pad(2)}}.txt")]
pub input_location: String,
/// A formatter that will be used to get the path for the solution file.
#[arg(short, long, default_value = "aoc_{year}/src/day_{day:pad}.rs")]
#[arg(short, long, default_value = "aoc_{{year}}/src/day_{{day:pad(2)}}.rs")]
solution_location: String,
/// Location formatter of the file importing each solution module.
#[arg(long, default_value = "aoc_{year}/src/lib.rs")]
#[arg(long, default_value = "aoc_{{year}}/src/lib.rs")]
module_location: String,
/// A formatter for a new line that will be added to the module file before the marker.
#[arg(long, default_values_t = ["mod day_{day:pad};".to_owned(), "&day_{day:pad}::Day{day:pad},".to_owned()])]
#[arg(long, default_values_t = ["mod day_{{day:pad(2)}};".to_owned(), "&day_{{day:pad(2)}}::Day{{day:pad(2)}},".to_owned()])]
module_templates: Vec<String>,
/// A marker is a string that will be found in the module file and is used to determine where to insert the new line.
/// If not provided, the default markers will be used.
Expand All @@ -72,11 +98,11 @@ pub struct InitArgs {
/// Don't create a solution file.
/// Useful if you want to use this command with a different language or organization.
#[arg(short, long)]
no_scaffold: bool,
pub no_scaffold: bool,

/// The day to fetch the input for.
day: u8,
pub day: u8,
/// The year to fetch the input for.
#[arg(default_value_t = current_year())]
year: u16,
pub year: u16,
}
50 changes: 47 additions & 3 deletions scaffold/src/commands/init.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,51 @@
use std::{
fs::{self, File},
io::Write,
path::Path,
};

use anyhow::Result;
use url::Url;

use crate::{
args::{Args, InitArgs},
formatter::Formatter,
misc::current_year,
session::{Authenticated, Session},
};

pub fn init(session: &Session, cmd: &InitArgs, args: &Args) -> Result<()> {
write_input(session, cmd, args)?;
Ok(())
}

fn write_input(session: &Session, cmd: &InitArgs, args: &Args) -> Result<()> {
let file_location = Formatter::new(&cmd.input_location)?
.format::<&[_]>(&[("year", cmd.year), ("day", cmd.day as u16)])?;

let path = Path::new(&file_location);
if let Some(parent) = path.parent() {
if !parent.exists() {
fs::create_dir_all(parent)?;
}
}

let mut file = File::create(path)?;
let input = fetch_input(session, &args.address, cmd.day, Some(cmd.year))?;
file.write_all(input.as_bytes())?;
println!("[*] Wrote input to {file_location}");
Ok(())
}

fn fetch_input(session: &Session, base: &Url, day: u8, year: Option<u16>) -> Result<String> {
let year = year.unwrap_or_else(current_year);
println!("[*] Fetching input for {day}/{year}");

use crate::session::Session;
let url = base.join(&format!("{year}/day/{day}/input"))?;
let body = ureq::get(url.as_str())
.authenticated(session)
.call()?
.into_string()?;

pub fn init(session: &Session, day: u8, year: Option<u16>) -> Result<()> {
todo!()
Ok(body)
}
12 changes: 6 additions & 6 deletions scaffold/src/commands/timer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,24 @@ use crate::args::TimerArgs;
/// The timezone of the Advent of Code release.
const AOC_TIMEZONE: u32 = 5;

pub fn timer(timer: TimerArgs) -> Result<()> {
pub fn timer(cmd: &TimerArgs) -> Result<()> {
let mut stop_time = next_release()?;

if let Some(offset) = timer.offset {
if let Some(offset) = cmd.offset {
stop_time = stop_time + chrono::Duration::seconds(offset as i64);
}

if Utc::now() >= stop_time {
println!("[*] The next puzzle has already been released.");

if timer.offset.is_some() {
if cmd.offset.is_some() {
println!("[*] Note: This may be because of the offset you set");
}

return Ok(());
}

if timer.quiet {
if cmd.quiet {
println!("[*] Waiting...");
} else {
println!("[*] The next puzzle will be released in:");
Expand All @@ -41,14 +41,14 @@ pub fn timer(timer: TimerArgs) -> Result<()> {
break;
}

if !timer.quiet {
if !cmd.quiet {
let time_left = (stop_time - now).to_std()?;
let time_left = Duration::new(time_left.as_secs(), 0);
print!("\r\x1b[0K[*] {}", humantime::format_duration(time_left));
io::stdout().flush()?;
}

thread::sleep(Duration::from_secs_f32(timer.frequency));
thread::sleep(Duration::from_secs_f32(cmd.frequency));
}

Ok(())
Expand Down
20 changes: 12 additions & 8 deletions scaffold/src/commands/token.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
use anyhow::Result;
use url::Url;

use crate::{commands::verify::verify_inner, session::Session, TOKEN_VAR};
use crate::{
args::{Args, TokenArgs},
commands::verify::verify_inner,
session::Session,
TOKEN_VAR,
};

pub fn token(session: &Option<Session>, token: String, url: &Url) -> Result<()> {
if token.len() != 128 {
anyhow::bail!("Invalid token length of {}, should be 128", token.len());
pub fn token(session: &Option<Session>, cmd: &TokenArgs, args: &Args) -> Result<()> {
if cmd.token.len() != 128 {
anyhow::bail!("Invalid token length of {}, should be 128", cmd.token.len());
}

println!("[*] Validating session token...");
let new_session = Session::new(token.clone());
verify_inner(&new_session, url)?;
let new_session = Session::new(&cmd.token);
verify_inner(&new_session, &args.address)?;
println!("[*] Session token is valid.");

if session.is_some() && session.as_ref().unwrap().is_from_env() {
Expand All @@ -19,6 +23,6 @@ pub fn token(session: &Option<Session>, token: String, url: &Url) -> Result<()>
println!("[*] Setting session token");
}

globalenv::set_var(TOKEN_VAR, &token)?;
globalenv::set_var(TOKEN_VAR, &cmd.token)?;
Ok(())
}
Loading

0 comments on commit 03deb5d

Please sign in to comment.