diff --git a/adventofcode/aoc.py b/adventofcode/aoc.py index 1149618..5750be0 100644 --- a/adventofcode/aoc.py +++ b/adventofcode/aoc.py @@ -1,21 +1,49 @@ import argparse from pathlib import Path -from adventofcode.helper import run, get_input_file, get_auth +from adventofcode.helper import ( + MAX_YEAR, + MIN_YEAR, + get_auth, + get_input_file, + get_max_day, + run, + run_all, +) TEMPLATE = """#!/usr/bin/env python3 +import fileinput + + def main(inp): for l in inp: print(l) if __name__ == '__main__': - import fileinput lines = [x.rstrip() for x in fileinput.input()] main(lines) """ +def year_or_all(value): + """Custom type function to validate 'year' as either an integer or 'all'.""" + if value.lower() == "all": + return value + try: + year = int(value) + if 2015 <= year <= 2025: + return year + else: + raise argparse.ArgumentTypeError( + f"Year must be between 2015 and 2025. Got: {year}" + ) + except ValueError: + raise argparse.ArgumentTypeError( + f"Invalid value: {value}. Must be an integer or 'all'." + ) + + def main(): parser = argparse.ArgumentParser(description="Advent of Code CLI") subparsers = parser.add_subparsers(dest="command") @@ -25,13 +53,15 @@ def main(): init_parser.add_argument("day", type=int) run_parser = subparsers.add_parser("run", help="Run an aoc day") - run_parser.add_argument("year", type=int) + run_parser.add_argument("year", type=year_or_all) run_parser.add_argument("day", type=int, nargs="?", default=None) args = parser.parse_args() if args.command == "init": handle_init(args.year, args.day) + elif args.command == "run" and args.year == "all": + handle_run_all() elif args.command == "run": handle_run(args.year, args.day) else: @@ -39,8 +69,13 @@ def main(): def handle_init(year, day): - if not 1 <= day <= 25: - print(f"Invalid day: {day}. Must be between 1 and 25.") + if not MIN_YEAR <= year <= MAX_YEAR: + print(f"Invalid year: {year}. Must be between {MIN_YEAR} and {MAX_YEAR}.") + return + + max_day = get_max_day(year) + if not 1 <= day <= max_day: + print(f"Invalid day: {day}. Must be between 1 and {max_day}.") return root = Path(__file__).parent @@ -69,5 +104,9 @@ def handle_run(year, day): run(year, day) +def handle_run_all(): + run_all() + + if __name__ == "__main__": main() diff --git a/adventofcode/helper.py b/adventofcode/helper.py index 6fce493..e2f255e 100644 --- a/adventofcode/helper.py +++ b/adventofcode/helper.py @@ -1,13 +1,21 @@ #!/usr/bin/env python3 -import urllib.request +import os +import subprocess import sys import time -import subprocess -import os +import urllib.request from pathlib import Path +MIN_YEAR = 2015 +MAX_YEAR = 2025 ROOTPATH = Path(os.path.dirname(os.path.realpath(__file__))) + +def get_max_day(year): + # starting with 2025, advent of code only has 12 days + return 25 if year < 2025 else 12 + + _auth = None @@ -54,11 +62,30 @@ def get_input_file(year, day): return res +def resolve_paths(year, day): + path = ROOTPATH / Path(f"{year}/day{day}") + script_path = path / Path(f"day{day}.py") + input_path = path / Path("input.txt") + return script_path, input_path + + +def run_all(): + for year in range(2015, MAX_YEAR + 1): + print(f"Running year {year}") + run(year, None) + + def run(year, day): + if not MIN_YEAR <= year <= MAX_YEAR: + print(f"Invalid year {year}", file=sys.stderr) + exit(1) + if day is not None: - path = ROOTPATH / Path(f"{year}/day{day}") - script_path = path / Path(f"day{day}.py") - input_path = path / Path("input.txt") + if not 1 <= day <= get_max_day(year): + print(f"Invalid day {day}", file=sys.stderr) + exit(1) + + script_path, input_path = resolve_paths(year, day) if not script_path.exists(): print(f"Invalid day {day}", file=sys.stderr) exit(1) @@ -72,9 +99,7 @@ def run(year, day): run_day(script_path, input_path) else: for day in range(1, 26): - path = ROOTPATH / Path(f"{year}/day{day}") - script_path = path / Path(f"day{day}.py") - input_path = path / Path("input.txt") + script_path, input_path = resolve_paths(year, day) if script_path.exists(): if not input_path.exists(): print(f"- downloading input file {input_path}")