aoc_star/
lib.rs

1//! # AOC Star
2//!
3//! `aoc-star` is a small library and CLI helper to organize and run your
4//! [Advent of Code](https://adventofcode.com/) solutions.
5//!
6//! It provides:
7//!
8//! - An attribute macro `#[aoc_star::star(...)]` to register solution functions
9//!   for a given `day`, `part`, and optional `year`.
10//! - A tiny “runner” that looks up the appropriate solution based on CLI
11//!   arguments and executes it, wiring up input loading and (optionally)
12//!   answer submission via `aoc-client`.
13//!
14//! ## Quick example
15//!
16//! Add `aoc-star` to your `Cargo.toml`:
17//!
18//! ```toml
19//! [dependencies]
20//! aoc-star = "0.1"
21//! ```
22//!
23//! Then in your binary crate (for example `src/bin/aoc.rs`):
24//!
25//! ```no_run
26//! use aoc_star::star;
27//!
28//! #[star(day = 1, part = 1, year = 2024)]
29//! fn day1_part1(input: String) -> String {
30//!     // Solve the puzzle here using the contents of `input`
31//!     input.lines().count().to_string()
32//! }
33//!
34//! fn main() -> Result<(), Box<dyn std::error::Error>> {
35//!     aoc_star::run()
36//! }
37//! ```
38//!
39//! Running this binary like
40//!
41//! ```sh
42//! cargo run -- --day 1 --part 1 --year 2024
43//! ```
44//!
45//! will look up the registered solution for day 1, part 1, year 2024, fetch
46//! the input (or read it from a file if you pass `--input-file`), and print
47//! the resulting answer.
48//!
49//! ## Features
50//!
51//! - `aoc-client` (optional): enable remote input fetching and answer
52//!   submission using the [`aoc-client`](https://crates.io/crates/aoc-client)
53//!   crate. When disabled you must always provide an `--input-file`.
54//! - `test-helpers`: re-exports some internals (`CommandArgument` and
55//!   `run_with_result`) to make integration testing easier.
56//!
57//!! ### `star` macro
58//!
59//! The `#[star(day = X, part = Y, year = Z)]` attribute macro registers
60//! the annotated function as the solution for the specified day, part, and
61//! optional year. The function must have the signature `fn(String) -> String`,
62//! where the input `String` contains the puzzle input, and the returned
63//! `String` is the answer.
64//!
65//! If the `year` parameter is omitted, the solution is considered
66//! year-agnostic and will be used for any year that does not halve a more specific solution.
67//!
68//! ### CLI arguments
69//!
70//! The `run` function parses the following command line arguments:
71//!
72//! - `--day <DAY>`: The Advent of Code day (1–25).
73//! - `--part <PART>`: The puzzle part (usually 1 or 2).
74//! - `--year <YEAR>`: The Advent of Code year (e.g., 2024). Defaults to
75//!   the current year if not provided.
76//! - `--input-file <FILE>`: Path to a file containing the puzzle input.
77//!   This argument is required if the `aoc-client` feature is not enabled.
78//! - `--publish`: If provided and the `aoc-client` feature is enabled,
79//!   the computed answer will be submitted to Advent of Code.
80//!
81//! The default year is either the one on the config file or the current year.
82//! The config contains the session cookie needed to fetch inputs and publish answers and
83//! the default year. If not present, the config file can be created by running
84//! this command with the `--setup` flag. It will be located at `$XDG_CONFIG_HOME/aoc-star/config.toml`
85//! which in linux systems usually resolves to `~/.config/aoc-star/config.toml`.
86//!
87//! ## License
88
89mod cli;
90mod config;
91mod runner;
92
93// Re-export the star macro so users can just `use aoc_star::star;`.
94pub use aoc_star_derive::star;
95
96use clap::Parser;
97// This re-export is unfortunately necessary because
98// the macro expansions of the `aoc_star_derive::star` macro
99// need to access the `inventory` crate from the same namespace
100// as this crate. There may be a better way to handle this in the future.
101pub use inventory;
102
103use crate::runner::run_with_result;
104
105#[cfg(any(test, feature = "test-helpers"))]
106pub mod test_helpers {
107    //! Helpers intended for testing `aoc-star` or crates that use it.
108    //!
109    //! This module is only available when the `test-helpers` feature is
110    //! enabled
111    //!
112    //! It re-exports:
113    //! - [`CommandArgument`](crate::cli::CommandArgument): the parsed CLI
114    //!   arguments structure.
115    //! - [`run_with_result`](crate::runner::run_with_result): the
116    //!   programmatic entry point used by [`run`].
117    pub use crate::cli::CommandArgument;
118    pub use crate::runner::run_with_result;
119}
120
121/// A registered Advent of Code solution.
122///
123/// Instances of this type are created by the `#[star]` attribute macro in the
124/// `aoc-star-derive` crate and collected via the [`inventory`] crate. You
125/// usually do not construct this type directly.
126///
127/// - `day`: The Advent of Code day (1–25).
128/// - `part`: The part of the puzzle (1 or 2).
129/// - `year`: The Advent of Code year; if `None`, the solution is considered
130///   year-agnostic and will be used for any year that does not have a
131///   more specific solution.
132/// - `func`: The solution function, which must take the puzzle input as a
133///   `String` and return the answer as a `String`.
134pub struct AocEntry {
135    /// Advent of Code day number (1–25).
136    pub day: u32,
137    /// Puzzle part (usually `1` or `2`).
138    pub part: u32,
139    /// Advent of Code year, or `None` for year-agnostic solutions.
140    pub year: Option<i32>,
141    /// The solution function that processes the puzzle input and returns the answer.
142    pub func: fn(String) -> String,
143}
144
145crate::inventory::collect!(AocEntry);
146
147/// Run the Advent of Code solution based on command line arguments.
148///
149/// This should be used in the `main` function of the binary crate that wires
150/// together your solutions. It:
151///
152/// 1. Parses the command line using [`clap`].
153/// 2. Locates the registered solution for the requested day/part/year.
154/// 3. Loads the input (either from `--input-file` or, if the `aoc-client`
155///    feature is enabled, remotely from Advent of Code).
156/// 4. Optionally publishes the answer when `--publish` is used and
157///    the `aoc-client` feature is enabled.
158/// 5. Prints the resulting answer to stdout.
159///
160/// # Errors
161///
162/// Returns an error if input reading or (when enabled) communication with
163/// Advent of Code fails. If no matching solution is found, this function
164/// will panic.
165///
166/// # Examples
167///
168/// ```no_run
169/// fn main() -> Result<(), Box<dyn std::error::Error>> {
170///     aoc_star::run()
171/// }
172/// ```
173pub fn run() -> Result<(), Box<dyn std::error::Error>> {
174    // We get the command line arguments
175    let command_argument = cli::CommandArgument::parse();
176    if command_argument.setup {
177        config::setup_config_prompt()?;
178        println!("Configuration file created successfully.");
179        return Ok(());
180    }
181    let result = run_with_result(command_argument)?;
182    println!("{result}");
183    Ok(())
184}