advent_of_utils_cli/
input.rs1use crate::error::AocError;
2use crate::types::AocDatabase;
3use reqwest::blocking::Client;
4use std::{fmt::Display, sync::Arc, time::Instant};
5
6use crate::error::InputError;
7
8const USER_AGENT: &str =
9 "Advent of Utils by Itron-al-Lenn found on github.com/Itron-al-Lenn/Advent-of-Utils";
10const AOC_BASE_URL: &str = "https://adventofcode.com";
11
12#[derive(Debug, Clone)]
13pub struct SessionToken(String);
14
15impl SessionToken {
16 pub fn new() -> Result<Self, InputError> {
17 match std::env::var("AOC_SESSION") {
18 Ok(token) => Ok(Self(token)),
19 Err(e) => Err(InputError::VarError {
20 key: "AOC_SESSION".to_string(),
21 reason: "Faield fetching the session token".to_string(),
22 source: Some(e),
23 }),
24 }
25 }
26}
27
28impl Display for SessionToken {
29 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 write!(f, "{}", self.0)
31 }
32}
33
34impl From<String> for SessionToken {
35 fn from(value: String) -> Self {
36 Self(value)
37 }
38}
39
40fn create_client() -> Result<Client, InputError> {
41 let cookie = reqwest::cookie::Jar::default();
42 cookie.add_cookie_str(
43 &format!("session={}", SessionToken::new()?),
44 &AOC_BASE_URL.parse::<reqwest::Url>().unwrap(),
45 );
46
47 Ok(Client::builder()
48 .cookie_provider(Arc::new(cookie))
49 .user_agent(USER_AGENT)
50 .build()
51 .expect("Failed to create HTTP client"))
52}
53
54fn fetch_input(year: i32, day: u8) -> Result<String, InputError> {
55 let url = format!("{}/{}/day/{}/input", AOC_BASE_URL, year, day);
56
57 create_client()?
58 .get(&url)
59 .send()
60 .map_err(|e| InputError::FetchFailed {
61 year,
62 day,
63 reason: "Network request failed".to_string(),
64 source: Some(e),
65 })?
66 .error_for_status()
67 .map_err(|e| InputError::FetchFailed {
68 year,
69 day,
70 reason: "Server returned error status".to_string(),
71 source: Some(e),
72 })?
73 .text()
74 .map_err(|e| InputError::FetchFailed {
75 year,
76 day,
77 reason: "Failed to read response text".to_string(),
78 source: Some(e),
79 })
80}
81
82pub fn get_input(
83 year: i32,
84 day: u8,
85 db: &AocDatabase,
86 test: bool,
87) -> Result<(String, Instant), AocError> {
88 if db.has_input(year, day, test)? {
89 let time = Instant::now();
90 let input = db.get_input(year, day, test)?;
91 Ok((input, time))
92 } else if !test {
93 println!("Fetching online...");
94 let input = fetch_input(year, day)?;
95 let time = Instant::now();
96 db.set_input(year, day, test, input.clone())?;
97 Ok((input, time))
98 } else {
99 Err(AocError::Input(InputError::NoTestInput { day }))
100 }
101}