summaryrefslogtreecommitdiff
path: root/src/cli.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/cli.rs')
-rw-r--r--src/cli.rs62
1 files changed, 30 insertions, 32 deletions
diff --git a/src/cli.rs b/src/cli.rs
index 154771b..5199e4c 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -3,7 +3,7 @@
//! This module supports running `pilcrow` as a freestanding program, via the
//! [`Args`] struct.
-use std::{future, io, str};
+use std::{future, str};
use axum::{
http::header,
@@ -15,10 +15,13 @@ use sqlx::sqlite::SqlitePool;
use tokio::net;
use web_push::{IsahcWebPushClient, WebPushClient};
+pub use crate::exit::Exit;
use crate::{
app::App,
- clock, db, routes,
- umask::{self, Umask},
+ clock, db,
+ error::failed::{Failed, ResultExt as _},
+ routes,
+ umask::Umask,
};
/// Command-line entry point for running the `pilcrow` server.
@@ -96,20 +99,24 @@ impl Args {
/// of the failure.
pub async fn run(self) -> Result<(), impl std::error::Error> {
self.umask.set();
- let pool = self.pool().await?;
+ let pool = self.pool().await.fail("Failed to create database pool")?;
- let webpush = IsahcWebPushClient::new()?;
+ let webpush = IsahcWebPushClient::new().fail("Failed to create web push publisher")?;
let app = App::from(pool, webpush);
match self.command {
None => self.serve(app).await?,
- Some(Command::RotateVapidKey) => app.vapid().rotate_key().await?,
+ Some(Command::RotateVapidKey) => app
+ .vapid()
+ .rotate_key()
+ .await
+ .fail("Failed to rotate VAPID key")?,
}
- Result::<_, Error>::Ok(())
+ Result::<_, Failed>::Ok(())
}
- async fn serve<P>(self, app: App<P>) -> Result<(), Error>
+ async fn serve<P>(self, app: App<P>) -> Result<(), Failed>
where
P: WebPushClient + Clone + Send + Sync + 'static,
{
@@ -118,24 +125,25 @@ impl Args {
.route_layer(middleware::map_response(Self::server_info()))
.with_state(app);
- let listener = self.listener().await?;
- let started_msg = started_msg(&listener)?;
+ let listen_addr = self.listen_addr();
+ let listener = net::TcpListener::bind(&listen_addr).await.fail_with(|| {
+ format!(
+ "Failed to bind to {host}:{port}",
+ host = listen_addr.0,
+ port = listen_addr.1
+ )
+ })?;
+ let started_msg = started_msg(&listen_addr);
let serve = axum::serve(listener, app);
println!("{started_msg}");
- serve.await?;
+ serve.await.fail("Failed to serve application")?;
Ok(())
}
- async fn listener(&self) -> io::Result<net::TcpListener> {
- let listen_addr = self.listen_addr();
- let listener = tokio::net::TcpListener::bind(listen_addr).await?;
- Ok(listener)
- }
-
- fn listen_addr(&self) -> impl net::ToSocketAddrs + '_ {
- (self.address.as_str(), self.port)
+ fn listen_addr(&self) -> (String, u16) {
+ (self.address.clone(), self.port)
}
async fn pool(&self) -> Result<SqlitePool, db::Error> {
@@ -154,17 +162,7 @@ impl Args {
}
}
-fn started_msg(listener: &net::TcpListener) -> io::Result<String> {
- let local_addr = listener.local_addr()?;
- Ok(format!("listening on http://{local_addr}/"))
-}
-
-#[derive(Debug, thiserror::Error)]
-#[error(transparent)]
-enum Error {
- Io(#[from] io::Error),
- Database(#[from] db::Error),
- Sqlx(#[from] sqlx::Error),
- Umask(#[from] umask::Error),
- Webpush(#[from] web_push::WebPushError),
+fn started_msg(listen_addr: &(String, u16)) -> String {
+ let (host, port) = listen_addr;
+ format!("Listening on http://{host}:{port}/")
}