diff --git a/crates/synd_term/src/application/cache/mod.rs b/crates/synd_term/src/application/cache/mod.rs index ba96873f..0cf8306d 100644 --- a/crates/synd_term/src/application/cache/mod.rs +++ b/crates/synd_term/src/application/cache/mod.rs @@ -1,12 +1,37 @@ -use std::{borrow::Borrow, io, path::PathBuf}; +use std::{ + borrow::Borrow, + io, + path::{Path, PathBuf}, +}; + +use serde::{de::DeserializeOwned, Serialize}; +use thiserror::Error; use crate::{ - auth::{Credential, CredentialError, Unverified}, + auth::{Credential, Unverified}, config, filesystem::{fsimpl, FileSystem}, ui::components::gh_notifications::GhNotificationFilterOptions, }; +#[derive(Debug, Error)] +pub enum PersistCacheError { + #[error("io error: {path} {io} ")] + Io { path: PathBuf, io: io::Error }, + #[error("serialize error: {0}")] + Serialize(#[from] serde_json::Error), +} + +#[derive(Debug, Error)] +pub enum LoadCacheError { + #[error("cache entry not found")] + NotFound, + #[error("io error: {path} {io}")] + Io { path: PathBuf, io: io::Error }, + #[error("deserialize error: {0}")] + Deserialize(#[from] serde_json::Error), +} + pub struct Cache { dir: PathBuf, fs: FS, @@ -31,67 +56,74 @@ where /// Persist credential in filesystem. /// This is blocking operation. - pub fn persist_credential(&self, cred: impl Borrow) -> Result<(), CredentialError> { - let cred = cred.borrow(); - let path = self.credential_file(); - - self.fs.create_dir_all(self.dir.as_path()).map_err(|err| { - CredentialError::PersistCredential { - io_err: err, - path: self.dir.clone(), - } - })?; - - let mut file = self - .fs - .create_file(&path) - .map_err(|err| CredentialError::PersistCredential { io_err: err, path })?; - - serde_json::to_writer(&mut file, cred).map_err(CredentialError::Serialize) - } - - /// Load credential from filesystem. - /// This is blocking operation. - pub fn load_credential(&self) -> Result, CredentialError> { - let path = self.credential_file(); - - let mut file = self - .fs - .open_file(&path) - .map_err(|err| CredentialError::Open { io_err: err, path })?; - - serde_json::from_reader::<_, Credential>(&mut file) - .map_err(CredentialError::Deserialize) - .map(Unverified::from) - } - - fn credential_file(&self) -> PathBuf { - self.dir.join(config::cache::CREDENTIAL_FILE) + pub fn persist_credential( + &self, + cred: impl Borrow, + ) -> Result<(), PersistCacheError> { + self.persist(&self.credential_file(), cred.borrow()) } pub(crate) fn persist_gh_notification_filter_options( &self, options: impl Borrow, - ) -> anyhow::Result<()> { - let options = options.borrow(); - let path = self.gh_notification_filter_option_file(); + ) -> Result<(), PersistCacheError> { + self.persist(&self.gh_notification_filter_option_file(), options.borrow()) + } - self.fs.create_dir_all(self.dir.as_path())?; + fn persist(&self, path: &Path, entry: &T) -> Result<(), PersistCacheError> + where + T: ?Sized + Serialize, + { + if let Some(parent) = path.parent() { + self.fs + .create_dir_all(parent) + .map_err(|err| PersistCacheError::Io { + path: parent.to_path_buf(), + io: err, + })?; + } - let mut file = self.fs.create_file(path)?; + self.fs + .create_file(path) + .map_err(|err| PersistCacheError::Io { + path: path.to_path_buf(), + io: err, + }) + .and_then(|mut file| { + serde_json::to_writer(&mut file, entry).map_err(PersistCacheError::Serialize) + }) + } - serde_json::to_writer(&mut file, options).map_err(anyhow::Error::from) + /// Load credential from filesystem. + /// This is blocking operation. + pub fn load_credential(&self) -> Result, LoadCacheError> { + self.load::(&self.credential_file()) + .map(Unverified::from) } pub(crate) fn load_gh_notification_filter_options( &self, - ) -> anyhow::Result { - let path = self.gh_notification_filter_option_file(); + ) -> Result { + self.load(&self.gh_notification_filter_option_file()) + } - let mut file = self.fs.open_file(path)?; + fn load(&self, path: &Path) -> Result + where + T: DeserializeOwned, + { + self.fs + .open_file(path) + .map_err(|err| LoadCacheError::Io { + io: err, + path: path.to_path_buf(), + }) + .and_then(|mut file| { + serde_json::from_reader::<_, T>(&mut file).map_err(LoadCacheError::Deserialize) + }) + } - serde_json::from_reader::<_, GhNotificationFilterOptions>(&mut file) - .map_err(anyhow::Error::from) + fn credential_file(&self) -> PathBuf { + self.dir.join(config::cache::CREDENTIAL_FILE) } fn gh_notification_filter_option_file(&self) -> PathBuf { diff --git a/crates/synd_term/src/application/mod.rs b/crates/synd_term/src/application/mod.rs index be5a52aa..996b77e7 100644 --- a/crates/synd_term/src/application/mod.rs +++ b/crates/synd_term/src/application/mod.rs @@ -59,7 +59,7 @@ mod clock; pub use clock::{Clock, SystemClock}; mod cache; -pub use cache::Cache; +pub use cache::{Cache, LoadCacheError, PersistCacheError}; mod builder; pub use builder::ApplicationBuilder; diff --git a/crates/synd_term/src/auth/mod.rs b/crates/synd_term/src/auth/mod.rs index 47aa3eac..b24c3522 100644 --- a/crates/synd_term/src/auth/mod.rs +++ b/crates/synd_term/src/auth/mod.rs @@ -1,9 +1,8 @@ use std::{ borrow::Borrow, cmp::Ordering, - fmt, io, + fmt, ops::{Deref, Sub}, - path::PathBuf, }; use chrono::{DateTime, Utc}; @@ -13,7 +12,7 @@ use thiserror::Error; use tracing::debug; use crate::{ - application::{Cache, JwtService}, + application::{Cache, JwtService, LoadCacheError, PersistCacheError}, config, types::Time, }; @@ -32,26 +31,14 @@ pub enum CredentialError { GoogleJwtExpired { refresh_token: String }, #[error("google jwt email not verified")] GoogleJwtEmailNotVerified, - #[error("failed to open: {path} :{io_err}")] - Open { - #[source] - io_err: std::io::Error, - path: PathBuf, - }, - #[error("serialize credential: {0}")] - Serialize(serde_json::Error), - #[error("deserialize credential: {0}")] - Deserialize(serde_json::Error), #[error("decode jwt: {0}")] DecodeJwt(JwtError), #[error("refresh jwt id token: {0}")] RefreshJwt(JwtError), - #[error("persist credential: {path} :{io_err}")] - PersistCredential { - #[source] - io_err: io::Error, - path: PathBuf, - }, + #[error("persist credential: {0}")] + PersistCredential(#[from] PersistCacheError), + #[error("load credential: {0}")] + LoadCredential(#[from] LoadCacheError), } #[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]