#![warn(clippy::pedantic)] use std::{collections::{HashMap, HashSet}, thread, sync::{Mutex, Arc}, time::Duration, process::ExitCode, fs, panic::catch_unwind, io::{BufWriter, Write}}; use chrono::{DateTime, Utc}; use config::{Config, User}; use query::update_feeds; use rss::Channel; use crate::{junction::{bundle_rss, gen_status}, hooks::run_hook}; mod config; mod query; mod junction; mod server; mod hooks; #[derive(Clone, Debug)] pub struct Feed { channel: Option, last_fetched: DateTime, error_message: Option, } pub struct State { rss: String, guids: HashSet, feeds: HashMap, status: Option, } fn main() -> ExitCode { let config = match load_config() { Ok(config) => config, Err(e) => { eprintln!("Error loading config: {}", e); return 1.into() } }; let guids = load_guids().unwrap_or_default(); let state = State { rss: "".into(), guids, feeds: HashMap::new(), status: None, }; let state = Arc::new(Mutex::new(state)); let server_address = format!("{}:{}", config.host, config.port); println!("Starting server at {}", server_address); let server_threads = server::start(&server_address, config.worker_threads, state.clone()); drop(server_threads); let sleep_duration = Duration::from_secs(60 * config.refresh_time); loop { let result = catch_unwind(|| { let mut guard = state.lock().unwrap(); update_feeds(&mut guard.feeds, &config); let (hookdata, bundle) = bundle_rss(&mut guard, &config); let status = if config.status_page { Some(gen_status(&guard.feeds)) } else { None }; if let Some(hook) = &config.hook { run_hook(hook.to_owned(), hookdata).unwrap(); } guard.status = status; guard.rss = bundle.to_string(); save_guids(&guard.guids).unwrap(); drop(guard); }); if result.is_err() { eprintln!("Error occured white updating"); } else { println!("Feeds updated"); } thread::sleep(sleep_duration); } } fn load_config() -> Result> { let content = fs::read_to_string("config.json")?; let config: Config = serde_json::from_str(&content)?; Ok(config) } fn load_guids() -> Result, Box> { let content = fs::read_to_string("guids")?; Ok(content.split("\n").filter(|x| x.len() > 0).map(str::to_owned).collect()) } fn save_guids(guids: &HashSet) -> Result<(), Box> { let file = fs::OpenOptions::new().create(true).write(true).open("guids")?; let mut writer = BufWriter::new(file); for guid in guids { writeln!(writer, "{}", guid)?; } Ok(()) }