/** * matrix-feedbot v0.1.0 * * Copyright (C) 2024 The 1312 Media Collective * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ use std::{ error::Error, sync::{Arc, Mutex, MutexGuard} }; use tokio::fs; use chrono::{ DateTime, Utc }; use tracing::{ warn, info, debug }; use serde_yaml::mapping::Mapping as FeedReaderState; #[derive(Debug)] pub struct FeedReaderStateDb { state: Mutex, filename: String } impl FeedReaderStateDb { pub async fn new(filename: &str) -> Result, Box> { info!("loading {}", filename); let db = Arc::new(FeedReaderStateDb { state: Mutex::new(Self::load(filename).await?), filename: String::from(filename) }); Ok(db.clone()) } fn lock_state(&self) -> MutexGuard { self.state.lock().unwrap_or_else(|e| { warn!("State db mutex has been poisoned, continuing..."); e.into_inner() }) } pub async fn set( &self, uri: &str, dt: DateTime ) -> Result<(), Box> { { debug!("Updating feed reader state"); self.lock_state().insert(uri.into(), dt.timestamp().into()); } self.persist().await } #[tracing::instrument(ret, level="debug")] pub fn get(&self, uri: &str) -> Option> { debug!("Retrieving state for feed {}", uri); match self.lock_state().get(uri) { Some(t) => DateTime::from_timestamp((*t).as_i64().unwrap(), 0), None => None } } #[tracing::instrument(ret, level="debug")] async fn load(state_file: &str) -> Result> { let serialized_state = fs::read_to_string(state_file).await?; let state: FeedReaderState = serde_yaml::from_str(&serialized_state)?; Ok(state) } #[tracing::instrument(ret, level="debug")] async fn persist(&self) -> Result<(), Box> { let serialized_state = serde_yaml::to_string(&self.state)?; fs::write(self.filename.clone(), &serialized_state.as_bytes()).await?; Ok(()) } }