use crate::auth::TokenStore;
use crate::events::EventBus;
use ecr_core::revision::Revision;
use ecr_store::NotmuchStore;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time::SystemTime;
use tokio::sync::RwLock;
#[derive(Clone)]
pub struct AppState {
pub store: Arc<NotmuchStore>,
pub tokens: Arc<RwLock<TokenStore>>,
pub events: EventBus,
pub read_only: bool,
token_path: Option<PathBuf>,
token_seen: Arc<RwLock<Option<Stamp>>>,
written: Arc<RwLock<Option<Revision>>>,
}
impl AppState {
pub fn new(store: Arc<NotmuchStore>, tokens: TokenStore, read_only: bool) -> Self {
Self {
store,
tokens: Arc::new(RwLock::new(tokens)),
events: EventBus::new(),
read_only,
token_path: None,
token_seen: Arc::new(RwLock::new(None)),
written: Arc::new(RwLock::new(None)),
}
}
pub fn with_token_file(mut self, path: PathBuf) -> Self {
self.token_seen = Arc::new(RwLock::new(changed_at(&path)));
self.token_path = Some(path);
self
}
pub async fn refresh_tokens(&self) {
let Some(path) = &self.token_path else { return };
let found = changed_at(path);
if *self.token_seen.read().await == found {
return;
}
match TokenStore::load(path) {
Ok(loaded) => {
*self.tokens.write().await = loaded;
*self.token_seen.write().await = found;
}
Err(error) => tracing::warn!(
path = %path.display(),
%error,
"could not re-read the token store; keeping the tokens already loaded"
),
}
}
pub async fn requires_auth(&self) -> bool {
!self.tokens.read().await.is_empty()
}
pub async fn note_own_write(&self, revision: &Revision) {
*self.written.write().await = Some(revision.clone());
}
pub async fn own_write(&self, observed: &Revision) -> bool {
self.written.read().await.as_ref() == Some(observed)
}
}
type Stamp = (SystemTime, u64);
fn changed_at(path: &Path) -> Option<Stamp> {
let meta = std::fs::metadata(path).ok()?;
Some((meta.modified().ok()?, meta.len()))
}