aoc_session/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use core::fmt;
4use std::fmt::{Debug, Display};
5
6/// The error type for this crate.
7#[derive(Debug, thiserror::Error)]
8pub enum Error {
9    #[error("No session cookie found")]
10    NoSessionCookieFound,
11    #[error("Rookie crate error: {0}")]
12    RookieError(anyhow::Error),
13}
14
15/// The result type for this crate.
16pub type Result<T> = core::result::Result<T, Error>;
17
18/// Value of the session cookie for Advent of Code.
19///
20/// For example, this value can be used to get access to the puzzle input.
21///
22///  # Examples
23///
24/// ## Debug-print the session cookie value to stdout:
25///
26/// ```
27/// use aoc_session::aoc_session;
28///
29/// let session_id: String = match aoc_session() {
30///     Ok(session) => format!("{session:?}"),
31///     Err(e) => panic!("Error: {e}"),
32/// };
33///
34/// assert!(session_id.starts_with("session="));
35/// println!("{}", session_id);
36/// ```
37///
38/// ## Convert the session cookie value to a [`String`]:
39///
40/// ```
41/// use aoc_session::aoc_session;
42///
43/// let session_id: String = match aoc_session() {
44///     Ok(session) => session.to_string(),
45///     Err(e) => panic!("Error: {e}"),
46/// };
47///
48/// assert!(session_id.len() > 0);
49/// assert!(!session_id.starts_with("session="));
50/// println!("My session ID: {}", session_id);
51/// ```
52///
53pub struct AocSession(String);
54
55impl AocSession {
56    #[cfg(test)]
57    pub fn new(session: impl ToString) -> Self {
58        let session = session.to_string();
59        for symbol in session.chars() {
60            if !('a'..='z').contains(&symbol) && !('0'..='9').contains(&symbol) {
61                panic!("Session cookie value must be a lowercase string that represents a base-16 number");
62            }
63        }
64        Self(session)
65    }
66}
67
68impl Debug for AocSession {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        write!(f, "session={}", self.0)
71    }
72}
73
74impl Display for AocSession {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        write!(f, "{}", self.0)
77    }
78}
79
80/// Get the session cookie for Advent of Code. Beware that this function works for all browsers
81/// supported by [`rookie`] but is slow.
82///
83/// # Examples
84///
85/// ## Debug-print the session cookie value to stdout:
86///
87/// ```
88/// use aoc_session::aoc_session;
89///
90/// let session_id: String = match aoc_session() {
91///     Ok(session) => format!("{session:?}"),
92///     Err(e) => panic!("Error: {e}"),
93/// };
94///
95/// assert!(session_id.starts_with("session="));
96/// println!("{}", session_id);
97/// ```
98///
99/// ## Convert the session cookie value to a [`String`]:
100///
101/// ```
102/// use aoc_session::aoc_session;
103///
104/// let session_id: String = match aoc_session() {
105///     Ok(session) => session.to_string(),
106///     Err(e) => panic!("Error: {e}"),
107/// };
108///
109/// assert!(session_id.len() > 0);
110/// println!("My session ID: {}", session_id);
111/// ```
112///
113pub fn aoc_session() -> Result<AocSession> {
114    let domains = Some(vec!["adventofcode.com"]); // set to None to get all
115    let cookies: Vec<_> = rookie::load(domains).map_err(Error::RookieError)?;
116    let session = cookies
117        .into_iter()
118        .find(|c| c.name == "session")
119        .ok_or(Error::NoSessionCookieFound)?;
120    Ok(AocSession(session.value))
121}
122
123#[cfg(test)]
124mod tests {
125    use super::*;
126
127    #[test]
128    fn print_session_cookie() {
129        let session = match aoc_session() {
130            Ok(session) => session,
131            Err(e) => panic!(
132                "Error: {e}.\nIf you haven't logged in to Advent of Code yet, please do so now."
133            ),
134        };
135        // session=25a16c7465645f5f286128b604b18e3d5a906611b3eac6740672d5e471a7ab0d3af049fb7363eadb2e07edfe51b600927ddd29b2311ea418ce366e8b9cf98dcc
136        println!("{:?}", session);
137    }
138
139    #[test]
140    fn check_debug_format() {
141        let session = AocSession::new("25a16c7465645f5f286128b604b18e3d5a906611b3eac6740672d5e471a7ab0d3af049fb7363eadb2e07edfe51b600927ddd29b2311ea418ce366e8b9cf98dcc");
142        assert_eq!(
143            format!("{:?}", session),
144            "session=25a16c7465645f5f286128b604b18e3d5a906611b3eac6740672d5e471a7ab0d3af049fb7363eadb2e07edfe51b600927ddd29b2311ea418ce366e8b9cf98dcc"
145        );
146    }
147
148    #[test]
149    fn check_to_string() {
150        let session = AocSession::new("25a16c7465645f5f286128b604b18e3d5a906611b3eac6740672d5e471a7ab0d3af049fb7363eadb2e07edfe51b600927ddd29b2311ea418ce366e8b9cf98dcc");
151        assert_eq!(
152            session.to_string(),
153            "25a16c7465645f5f286128b604b18e3d5a906611b3eac6740672d5e471a7ab0d3af049fb7363eadb2e07edfe51b600927ddd29b2311ea418ce366e8b9cf98dcc"
154        );
155    }
156}