Add somehow::Error wrapping anyhow::Error

This commit is contained in:
Joscha 2023-08-05 20:41:23 +02:00
parent a5c0552341
commit 2b4a5d4021
6 changed files with 73 additions and 53 deletions

View file

@ -5,6 +5,8 @@ use std::{fs, io::ErrorKind, path::Path, time::Duration};
use serde::Deserialize; use serde::Deserialize;
use tracing::{debug, info}; use tracing::{debug, info};
use crate::somehow;
mod default { mod default {
use std::time::Duration; use std::time::Duration;
@ -74,7 +76,7 @@ pub struct Config {
} }
impl Config { impl Config {
pub fn load(path: &Path) -> anyhow::Result<Self> { pub fn load(path: &Path) -> somehow::Result<Self> {
info!(path = %path.display(), "Loading config"); info!(path = %path.display(), "Loading config");
let config = match fs::read_to_string(path) { let config = match fs::read_to_string(path) {
Ok(str) => toml::from_str(&str)?, Ok(str) => toml::from_str(&str)?,

View file

@ -1,5 +1,6 @@
mod config; mod config;
mod recurring; mod recurring;
mod somehow;
mod state; mod state;
mod web; mod web;
@ -64,7 +65,7 @@ fn set_up_logging(verbose: u8) {
} }
} }
fn load_config(path: Option<PathBuf>) -> anyhow::Result<&'static Config> { fn load_config(path: Option<PathBuf>) -> somehow::Result<&'static Config> {
let config_path = path.unwrap_or_else(|| { let config_path = path.unwrap_or_else(|| {
ProjectDirs::from("de", "plugh", "tablejohn") ProjectDirs::from("de", "plugh", "tablejohn")
.expect("could not determine home directory") .expect("could not determine home directory")
@ -90,7 +91,7 @@ async fn wait_for_signal() -> io::Result<()> {
Ok(()) Ok(())
} }
async fn run() -> anyhow::Result<()> { async fn run() -> somehow::Result<()> {
let args = Args::parse(); let args = Args::parse();
set_up_logging(args.verbose); set_up_logging(args.verbose);
@ -112,7 +113,7 @@ async fn run() -> anyhow::Result<()> {
} }
#[tokio::main] #[tokio::main]
async fn main() -> anyhow::Result<()> { async fn main() -> somehow::Result<()> {
// Rust-analyzer struggles analyzing code in this function, so the actual // Rust-analyzer struggles analyzing code in this function, so the actual
// code lives in a different function. // code lives in a different function.
run().await run().await

View file

@ -9,10 +9,14 @@ use gix::{objs::Kind, traverse::commit::Info, ObjectId, Repository};
use sqlx::{Acquire, SqliteConnection, SqlitePool}; use sqlx::{Acquire, SqliteConnection, SqlitePool};
use tracing::{debug, info}; use tracing::{debug, info};
async fn get_all_commits_from_db(conn: &mut SqliteConnection) -> anyhow::Result<HashSet<ObjectId>> { use crate::somehow;
async fn get_all_commits_from_db(
conn: &mut SqliteConnection,
) -> somehow::Result<HashSet<ObjectId>> {
let hashes = sqlx::query!("SELECT hash FROM commits") let hashes = sqlx::query!("SELECT hash FROM commits")
.fetch(conn) .fetch(conn)
.err_into::<anyhow::Error>() .err_into::<somehow::Error>()
.and_then(|r| async move { r.hash.parse::<ObjectId>().map_err(|e| e.into()) }) .and_then(|r| async move { r.hash.parse::<ObjectId>().map_err(|e| e.into()) })
.try_collect::<HashSet<_>>() .try_collect::<HashSet<_>>()
.await?; .await?;
@ -23,11 +27,11 @@ async fn get_all_commits_from_db(conn: &mut SqliteConnection) -> anyhow::Result<
fn get_new_commits_from_repo( fn get_new_commits_from_repo(
repo: &Repository, repo: &Repository,
old: &HashSet<ObjectId>, old: &HashSet<ObjectId>,
) -> anyhow::Result<Vec<Info>> { ) -> somehow::Result<Vec<Info>> {
// Collect all references starting with "refs" // Collect all references starting with "refs"
let mut all_references: Vec<ObjectId> = vec![]; let mut all_references: Vec<ObjectId> = vec![];
for reference in repo.references()?.prefixed("refs")? { for reference in repo.references()?.prefixed("refs")? {
let reference = reference.map_err(|e| anyhow::anyhow!(e))?; let reference = reference.map_err(|e| somehow::Error(anyhow::anyhow!(e)))?;
let id = reference.into_fully_peeled_id()?; let id = reference.into_fully_peeled_id()?;
// Some repos *cough*linuxkernel*cough* have refs that don't point to // Some repos *cough*linuxkernel*cough* have refs that don't point to
@ -49,7 +53,7 @@ fn get_new_commits_from_repo(
Ok(new_commits) Ok(new_commits)
} }
async fn insert_new_commits(conn: &mut SqliteConnection, new: &[Info]) -> anyhow::Result<()> { async fn insert_new_commits(conn: &mut SqliteConnection, new: &[Info]) -> somehow::Result<()> {
for commit in new { for commit in new {
let hash = commit.id.to_string(); let hash = commit.id.to_string();
sqlx::query!("INSERT OR IGNORE INTO commits (hash) VALUES (?)", hash) sqlx::query!("INSERT OR IGNORE INTO commits (hash) VALUES (?)", hash)
@ -59,7 +63,7 @@ async fn insert_new_commits(conn: &mut SqliteConnection, new: &[Info]) -> anyhow
Ok(()) Ok(())
} }
async fn insert_new_commit_links(conn: &mut SqliteConnection, new: &[Info]) -> anyhow::Result<()> { async fn insert_new_commit_links(conn: &mut SqliteConnection, new: &[Info]) -> somehow::Result<()> {
for commit in new { for commit in new {
let child = commit.id.to_string(); let child = commit.id.to_string();
for parent in &commit.parent_ids { for parent in &commit.parent_ids {
@ -78,14 +82,14 @@ async fn insert_new_commit_links(conn: &mut SqliteConnection, new: &[Info]) -> a
Ok(()) Ok(())
} }
async fn mark_all_commits_as_old(conn: &mut SqliteConnection) -> anyhow::Result<()> { async fn mark_all_commits_as_old(conn: &mut SqliteConnection) -> somehow::Result<()> {
sqlx::query!("UPDATE commits SET new = 0") sqlx::query!("UPDATE commits SET new = 0")
.execute(conn) .execute(conn)
.await?; .await?;
Ok(()) Ok(())
} }
async fn track_main_branch(conn: &mut SqliteConnection, repo: &Repository) -> anyhow::Result<()> { async fn track_main_branch(conn: &mut SqliteConnection, repo: &Repository) -> somehow::Result<()> {
let Some(head) = repo.head_ref()? else { return Ok(()); }; let Some(head) = repo.head_ref()? else { return Ok(()); };
let name = head.inner.name.to_string(); let name = head.inner.name.to_string();
let hash = head.into_fully_peeled_id()?.to_string(); let hash = head.into_fully_peeled_id()?.to_string();
@ -99,7 +103,10 @@ async fn track_main_branch(conn: &mut SqliteConnection, repo: &Repository) -> an
Ok(()) Ok(())
} }
async fn update_tracked_refs(conn: &mut SqliteConnection, repo: &Repository) -> anyhow::Result<()> { async fn update_tracked_refs(
conn: &mut SqliteConnection,
repo: &Repository,
) -> somehow::Result<()> {
let tracked_refs = sqlx::query!("SELECT name, hash FROM tracked_refs") let tracked_refs = sqlx::query!("SELECT name, hash FROM tracked_refs")
.fetch_all(&mut *conn) .fetch_all(&mut *conn)
.await?; .await?;
@ -127,7 +134,7 @@ async fn update_tracked_refs(conn: &mut SqliteConnection, repo: &Repository) ->
Ok(()) Ok(())
} }
async fn update_commit_tracked_status(conn: &mut SqliteConnection) -> anyhow::Result<()> { async fn update_commit_tracked_status(conn: &mut SqliteConnection) -> somehow::Result<()> {
sqlx::query!( sqlx::query!(
" "
WITH RECURSIVE reachable(hash) AS ( WITH RECURSIVE reachable(hash) AS (
@ -146,7 +153,7 @@ SET tracked = (hash IN reachable)
Ok(()) Ok(())
} }
pub async fn update(db: &SqlitePool, repo: &Repository) -> anyhow::Result<()> { pub async fn update(db: &SqlitePool, repo: &Repository) -> somehow::Result<()> {
debug!("Updating repo"); debug!("Updating repo");
let mut tx = db.begin().await?; let mut tx = db.begin().await?;
let conn = tx.acquire().await?; let conn = tx.acquire().await?;

42
src/somehow.rs Normal file
View file

@ -0,0 +1,42 @@
use std::{error, fmt, result};
use axum::{
http::StatusCode,
response::{IntoResponse, Response},
};
/// Wrapper around [`anyhow::Error`] that implements additional type classes.
pub struct Error(pub anyhow::Error);
impl<E> From<E> for Error
where
E: error::Error + Send + Sync + 'static,
{
fn from(value: E) -> Self {
Self(anyhow::Error::from(value))
}
}
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl IntoResponse for Error {
fn into_response(self) -> Response {
(
StatusCode::INTERNAL_SERVER_ERROR,
format!("500 Internal Server Error\n\n{}", self.0),
)
.into_response()
}
}
pub type Result<T> = result::Result<T, Error>;

View file

@ -10,7 +10,7 @@ use sqlx::{
}; };
use tracing::{debug, info}; use tracing::{debug, info};
use crate::config::Config; use crate::{config::Config, somehow};
async fn open_db(db_path: &Path) -> sqlx::Result<SqlitePool> { async fn open_db(db_path: &Path) -> sqlx::Result<SqlitePool> {
let options = SqliteConnectOptions::new() let options = SqliteConnectOptions::new()
@ -46,7 +46,7 @@ async fn open_db(db_path: &Path) -> sqlx::Result<SqlitePool> {
Ok(pool) Ok(pool)
} }
fn open_repo(repo_path: &Path) -> anyhow::Result<ThreadSafeRepository> { fn open_repo(repo_path: &Path) -> somehow::Result<ThreadSafeRepository> {
info!(path = %repo_path.display(), "Opening repo"); info!(path = %repo_path.display(), "Opening repo");
Ok(ThreadSafeRepository::open(repo_path)?) Ok(ThreadSafeRepository::open(repo_path)?)
} }
@ -63,7 +63,7 @@ impl AppState {
config: &'static Config, config: &'static Config,
db_path: &Path, db_path: &Path,
repo_path: &Path, repo_path: &Path,
) -> anyhow::Result<Self> { ) -> somehow::Result<Self> {
Ok(Self { Ok(Self {
config, config,
db: open_db(db_path).await?, db: open_db(db_path).await?,

View file

@ -1,43 +1,11 @@
mod index; mod index;
mod r#static; mod r#static;
use std::{error, result}; use axum::{routing::get, Router, Server};
use axum::{ use crate::{somehow, state::AppState};
http::StatusCode,
response::{IntoResponse, Response},
routing::get,
Router, Server,
};
use crate::state::AppState; pub async fn run(state: AppState) -> somehow::Result<()> {
/// Anyhow-like error that also implements [`IntoResponse`].
pub struct Error(anyhow::Error);
impl<E> From<E> for Error
where
E: error::Error + Send + Sync + 'static,
{
fn from(value: E) -> Self {
Self(anyhow::Error::from(value))
}
}
impl IntoResponse for Error {
fn into_response(self) -> Response {
(
StatusCode::INTERNAL_SERVER_ERROR,
format!("500 Internal Server Error\n\n{}", self.0),
)
.into_response()
}
}
/// Anyhow-like result that also implements [`IntoResponse`].
pub type Result<T> = result::Result<T, Error>;
pub async fn run(state: AppState) -> anyhow::Result<()> {
// TODO Add text body to body-less status codes // TODO Add text body to body-less status codes
let app = Router::new() let app = Router::new()