use std::{ path::PathBuf, sync::Arc, time::{Duration, Instant}, }; use crate::{ db::{self, UploadId, UploadKind}, notifies::Notifies, post_queue, schemas::novel::Novel, schemas::sanity::ensure_slug, subs, }; #[derive(Debug)] pub struct App { db: db::Db, notifies: Arc, pub subs: subs::Subs, pub queue: post_queue::Queue, } fn measure(f: impl FnOnce() -> O) -> (O, Duration) { let start = Instant::now(); (f(), start.elapsed()) } impl App { pub fn begin_upload(&self, slug: &str, kind: UploadKind) -> eyre::Result<(UploadId, PathBuf)> { ensure_slug(slug)?; let entry = self.db.entry(slug); Ok(entry.start_upload(slug, kind)) } pub fn finish_upload(&self, slug: &str, id: UploadId, file_name: &str) -> eyre::Result<()> { ensure_slug(slug)?; let entry = self.db.entry(slug); entry.finish_upload(id, file_name)?; Ok(()) } } impl App { pub fn novel(&self, slug: &str) -> eyre::Result { ensure_slug(slug)?; Ok(self.db.entry(slug)) } pub fn enumerate_novels(&self) -> Vec<(String, db::Entry)> { self.db.enumerate() } pub fn add_novel(&mut self, slug: impl Into, novel: Novel) -> eyre::Result<()> { let slug = slug.into(); ensure_slug(&slug)?; let db_entry = self.db.entry(&slug); db_entry.init(&novel)?; self.queue.add(post_queue::Item { novel: slug, post_at: novel.post_at, }); // While storing permit seems unnecessary, but actually // it prevents race condition, so keep it. self.notifies.update.notify_one(); Ok(()) } } impl App { pub fn mark_posted_until(&mut self, ts: jiff::Zoned) { self.db.update_state(move |s| { s.posted_until = Some(ts); }); } pub fn reload_queue(&mut self) -> eyre::Result<()> { tracing::info!("loading full database..."); let old_entries = self.queue.len(); let (queue, dur) = measure(|| { let queue: post_queue::Queue = self .db .enumerate() .into_iter() .filter_map(|(slug, e)| { let result = e.read_info(); match result { Ok(n) => { if let Some(ref posted_until) = self.db.state().posted_until { if &n.post_at <= posted_until { return None; } } Some(Ok(post_queue::Item { novel: slug, post_at: n.post_at, })) } Err(e) => Some(Err(e)), } }) .collect::>()?; Ok::<_, eyre::Report>(queue) }); self.queue = queue?; tracing::info!( took = ?dur, old = old_entries, new = self.queue.len(), "loaded database and created queue", ); self.notifies.update.notify_one(); Ok(()) } pub fn new(db: db::Db, notifies: Arc) -> eyre::Result { let queue = post_queue::Queue::default(); let mut this = Self { db, queue, subs: subs::Subs::new(Arc::clone(¬ifies)), notifies, }; this.reload_queue()?; Ok(this) } }