1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
use std::error::Error; use std::fs::{create_dir_all, read_to_string, File}; use std::io::Write; use std::io::{stdin, stdout}; use std::path::PathBuf; use std::time::Instant; const BASE_URL: &str = "https://adventofcode.com"; const INPUT_DIR: &str = "input"; const CONN_TOKEN_FILE: &str = ".token"; pub fn get_input(year: u16, day: u8) -> Result<String, Box<dyn Error>> { let mut result = get_from_path_or_else(&input_path(year, day), || { let start = Instant::now(); let url = format!("{}/{}/day/{}/input", BASE_URL, year, day); let session_cookie = format!("session={}", get_conn_token()?); let resp = attohttpc::get(&url) .header(attohttpc::header::COOKIE, session_cookie) .send()?; let elapsed = start.elapsed(); crate::print_with_duration("downloaded input file", None, elapsed); resp.text() })?; if result.ends_with('\n') { result.pop(); } Ok(result) } fn input_path(year: u16, day: u8) -> String { format!("{}/{}/day{}.txt", INPUT_DIR, year, day) } fn get_from_path_or_else<E: Error>( path: &str, fallback: impl FnOnce() -> Result<String, E>, ) -> Result<String, E> { let from_path = read_to_string(path); if let Ok(res) = from_path { Ok(res.trim().to_string()) } else { let res = fallback()?; create_dir_all(PathBuf::from(path).parent().expect("no parent directory")) .and_then(|_| File::create(path)) .and_then(|mut file| file.write_all(res.as_bytes())) .unwrap_or_else(|err| eprintln!("could not write {}: {}", path, err)); Ok(res) } } fn get_conn_token() -> Result<String, std::io::Error> { get_from_path_or_else(CONN_TOKEN_FILE, || { let mut stdout = stdout(); write!(&mut stdout, "Write your connection token: ")?; stdout.flush()?; let mut output = String::new(); stdin().read_line(&mut output)?; let mut file = File::create(CONN_TOKEN_FILE)?; file.write_all(output.as_bytes())?; Ok(output.trim().to_string()) }) }