advent_of_code_data/
lib.rs

1#![doc = include_str!("../README.md")]
2use std::str::FromStr;
3
4use client::{Client, ClientError, WebClient};
5use data::CheckResult;
6
7pub mod cache;
8pub mod client;
9pub mod config;
10pub mod data;
11
12mod utils;
13
14/// Represents a day in an Advent of Code year. Days are typically in the range
15/// [1, 25].
16#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
17pub struct Day(pub usize);
18
19impl std::fmt::Display for Day {
20    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21        write!(f, "{}", self.0)
22    }
23}
24
25impl From<i32> for Day {
26    fn from(value: i32) -> Self {
27        assert!(value >= 0);
28        Day(value as usize)
29    }
30}
31
32impl From<u32> for Day {
33    fn from(value: u32) -> Self {
34        Day(value as usize)
35    }
36}
37
38/// Represents an Advent of Code year, which is a year in which there was at
39/// least one Advent of Code puzzle.
40#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
41pub struct Year(pub usize);
42
43impl std::fmt::Display for Year {
44    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45        write!(f, "{}", self.0)
46    }
47}
48
49impl From<i32> for Year {
50    fn from(value: i32) -> Self {
51        assert!(value >= 0);
52        Year(value as usize)
53    }
54}
55
56impl From<Year> for i32 {
57    fn from(value: Year) -> Self {
58        value.0 as i32
59    }
60}
61
62/// Advent of Code puzzles are split into two parts - `One` and `Two`. Both
63/// parts will take the same input but typically produce different answers.
64#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
65pub enum Part {
66    One,
67    Two,
68}
69
70impl std::fmt::Display for Part {
71    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72        write!(
73            f,
74            "{}",
75            match self {
76                Part::One => "One",
77                Part::Two => "Two",
78            }
79        )
80    }
81}
82
83/// Represents an Advent of Code integer or string puzzle answer. Answers may or
84/// may not be valid solutions.
85///
86/// ```
87/// use advent_of_code_data::Answer;
88///
89/// let string_answer = Answer::String("hello world".to_string());
90/// let int_answer = Answer::Int(42);
91/// ```
92///
93/// # Automatic Conversions
94/// `Answer` implements `From` for common string and integer types:
95///
96///   - `String`, &str` -> `Answer::String`
97///   - Numeric types (`i8`, `i16`, `i32`, `i64`, `isize`, `usize`, etc) -> `Answer::Int`
98///
99/// ```
100/// use advent_of_code_data::Answer;
101///
102/// let string_answer: Answer = "hello world".into();
103/// assert_eq!(string_answer, Answer::String("hello world".to_string()));
104///
105/// let int_answer: Answer = 42.into();
106/// assert_eq!(int_answer, Answer::Int(42));
107/// ```
108///
109/// # FromStr (string parsing)
110/// `Answer` supports string parsing for both integer and string values.
111///
112/// ```
113/// use advent_of_code_data::Answer;
114///
115/// let answer: Answer = "testing 123".parse::<Answer>().unwrap();
116/// assert_eq!(answer, Answer::String("testing 123".to_string()));
117///
118/// let answer: Answer = "-5713".parse::<Answer>().unwrap();
119/// assert_eq!(answer, Answer::Int(-5713));
120/// ```
121#[derive(Clone, Debug, PartialEq)]
122pub enum Answer {
123    String(String),
124    Int(i128),
125}
126
127impl Answer {
128    pub fn to_i128(&self) -> Option<i128> {
129        match self {
130            Answer::String(_) => None,
131            Answer::Int(v) => Some(*v),
132        }
133    }
134}
135
136impl FromStr for Answer {
137    type Err = ();
138
139    fn from_str(s: &str) -> Result<Self, Self::Err> {
140        Ok(s.parse::<i128>()
141            .map_or_else(|_| Answer::String(s.to_string()), Answer::Int))
142    }
143}
144
145impl std::fmt::Display for Answer {
146    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147        match self {
148            Answer::String(v) => write!(f, "{}", v),
149            Answer::Int(v) => write!(f, "{}", v),
150        }
151    }
152}
153
154impl From<String> for Answer {
155    fn from(value: String) -> Self {
156        Self::String(value)
157    }
158}
159
160impl From<&str> for Answer {
161    fn from(value: &str) -> Self {
162        Self::String(value.to_string())
163    }
164}
165
166impl From<i8> for Answer {
167    fn from(value: i8) -> Self {
168        Self::Int(value as i128)
169    }
170}
171
172impl From<i16> for Answer {
173    fn from(value: i16) -> Self {
174        Self::Int(value as i128)
175    }
176}
177
178impl From<i32> for Answer {
179    fn from(value: i32) -> Self {
180        Self::Int(value as i128)
181    }
182}
183
184impl From<i64> for Answer {
185    fn from(value: i64) -> Self {
186        Self::Int(value as i128)
187    }
188}
189impl From<u8> for Answer {
190    fn from(value: u8) -> Self {
191        Self::Int(value as i128)
192    }
193}
194
195impl From<u16> for Answer {
196    fn from(value: u16) -> Self {
197        Self::Int(value as i128)
198    }
199}
200
201impl From<u32> for Answer {
202    fn from(value: u32) -> Self {
203        Self::Int(value as i128)
204    }
205}
206
207impl From<u64> for Answer {
208    fn from(value: u64) -> Self {
209        Self::Int(value as i128)
210    }
211}
212
213impl From<isize> for Answer {
214    fn from(value: isize) -> Self {
215        Self::Int(value as i128)
216    }
217}
218
219impl From<usize> for Answer {
220    fn from(value: usize) -> Self {
221        Self::Int(value as i128)
222    }
223}
224
225/// TODO: Good documentation.
226pub fn get_input(day: Day, year: Year) -> Result<String, ClientError> {
227    let client = WebClient::new()?;
228    client.get_input(day, year)
229}
230
231/// TODO: Good documentation.
232pub fn submit_answer(
233    answer: Answer,
234    part: Part,
235    day: Day,
236    year: Year,
237) -> Result<CheckResult, ClientError> {
238    let mut client: WebClient = WebClient::new()?;
239    client.submit_answer(answer, part, day, year)
240}
241
242#[cfg(test)]
243mod tests {
244    use super::*;
245
246    #[test]
247    fn print_year() {
248        let year = Year(2022);
249        assert_eq!(&format!("{year}"), "2022");
250    }
251
252    #[test]
253    fn print_day() {
254        let day = Day(22);
255        assert_eq!(&format!("{day}"), "22");
256    }
257
258    #[test]
259    fn print_part() {
260        assert_eq!(&format!("{}", Part::One), "One");
261        assert_eq!(&format!("{}", Part::Two), "Two");
262    }
263
264    #[test]
265    fn print_answer() {
266        assert_eq!(
267            &format!("{}", Answer::String("hello world".to_string())),
268            "hello world"
269        );
270
271        assert_eq!(&format!("{}", Answer::Int(42)), "42");
272    }
273
274    #[test]
275    #[allow(clippy::unnecessary_cast)]
276    fn int_answer_conversions() {
277        let answer: Answer = (-22 as i8).into();
278        assert_eq!(answer, Answer::Int(-22));
279
280        let answer: Answer = (-1317 as i16).into();
281        assert_eq!(answer, Answer::Int(-1317));
282
283        let answer: Answer = (-100_512 as i32).into();
284        assert_eq!(answer, Answer::Int(-100_512));
285
286        let answer: Answer = (-3_183_512_681 as i64).into();
287        assert_eq!(answer, Answer::Int(-3_183_512_681));
288
289        let answer: Answer = (22 as u8).into();
290        assert_eq!(answer, Answer::Int(22));
291
292        let answer: Answer = (1317 as u16).into();
293        assert_eq!(answer, Answer::Int(1317));
294
295        let answer: Answer = (100_512 as u32).into();
296        assert_eq!(answer, Answer::Int(100_512));
297
298        let answer: Answer = (3_183_512_681 as u64).into();
299        assert_eq!(answer, Answer::Int(3_183_512_681));
300    }
301
302    #[test]
303    fn string_answer_conversions() {
304        let answer: Answer = "hello world".to_string().into();
305        assert_eq!(answer, Answer::String("hello world".to_string()));
306
307        let answer: Answer = "testing 123".into();
308        assert_eq!(answer, Answer::String("testing 123".to_string()));
309    }
310
311    #[test]
312    fn parse_string_to_answer() {
313        let answer: Answer = "this is text".parse::<Answer>().unwrap();
314        assert_eq!(answer, Answer::String("this is text".to_string()));
315
316        let answer: Answer = "123".parse::<Answer>().unwrap();
317        assert_eq!(answer, Answer::Int(123));
318    }
319}