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}