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}