summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOwen Jacobson <owen@grimoire.ca>2024-09-25 01:22:07 -0400
committerOwen Jacobson <owen@grimoire.ca>2024-09-25 02:15:25 -0400
commit697e90f291cec1bbd89965c2731f9481ec4507c4 (patch)
treeb40ef7142da474b0ad2bd370b2c89d2008570976
parentaf7ece7dd5433051d67526ae15ad64f0f5b5e568 (diff)
rustdoc comment for the (very limited) public API of the crate.
This silences some `-Wclippy::pedantic` warning, and it's just a good thing to do. I've made the choice to have the docs comment face programmers, and to provide `hi --help` and `hi -h` content via Clap attributes instead of inferring it from the docs comment. Internal (private) "rustdoc" comments have been converted to regular comments until I learn how to write better rustdoc.
-rw-r--r--src/cli.rs56
-rw-r--r--src/clock.rs12
-rw-r--r--src/error.rs2
-rw-r--r--src/events/extract.rs4
-rw-r--r--src/lib.rs2
-rw-r--r--src/login/extract.rs20
-rw-r--r--src/login/repo/auth.rs6
-rw-r--r--src/main.rs2
-rw-r--r--src/repo/channel.rs3
-rw-r--r--src/repo/login/store.rs2
-rw-r--r--src/repo/message.rs2
-rw-r--r--src/repo/token.rs16
12 files changed, 89 insertions, 38 deletions
diff --git a/src/cli.rs b/src/cli.rs
index 729a791..b147f7d 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -1,3 +1,8 @@
+//! The `hi` command-line interface.
+//!
+//! This module supports running `hi` as a freestanding program, via the
+//! [`Args`] struct.
+
use std::io;
use axum::{middleware, Router};
@@ -7,22 +12,62 @@ use tokio::net;
use crate::{app::App, channel, clock, events, login, repo::pool};
-pub type Result<T> = std::result::Result<T, Error>;
-
+/// Command-line entry point for running the `hi` server.
+///
+/// This is intended to be used as a Clap [Parser], to capture command-line
+/// arguments for the `hi` server:
+///
+/// ```no_run
+/// # use hi::cli::Error;
+/// #
+/// # #[tokio::main]
+/// # async fn main() -> Result<(), Error> {
+/// use clap::Parser;
+/// use hi::cli::Args;
+///
+/// let args = Args::parse();
+/// args.run().await?;
+/// # Ok(())
+/// # }
+/// ```
#[derive(Parser)]
+#[command(
+ about = "Run the `hi` server.",
+ long_about = r#"Run the `hi` server.
+
+The database at `--database-url` will be created, or upgraded, automatically."#
+)]
pub struct Args {
+ /// The network address `hi` should listen on
#[arg(short, long, env, default_value = "localhost")]
address: String,
+ /// The network port `hi` should listen on
#[arg(short, long, env, default_value_t = 64209)]
port: u16,
+ /// Sqlite URL or path for the `hi` database
#[arg(short, long, env, default_value = "sqlite://.hi")]
database_url: String,
}
impl Args {
- pub async fn run(self) -> Result<()> {
+ /// Runs the `hi` server, using the parsed configuation in `self`.
+ ///
+ /// This will perform the following tasks:
+ ///
+ /// * Migrate the `hi` database (at `--database-url`).
+ /// * Start an HTTP server (on the interface and port controlled by
+ /// `--address` and `--port`).
+ /// * Print a status message.
+ /// * Wait for that server to shut down.
+ ///
+ /// # Errors
+ ///
+ /// Will return `Err` if the server is unable to start, or terminates
+ /// prematurely. The specific [`Error`] variant will expose the cause
+ /// of the failure.
+ pub async fn run(self) -> Result<(), Error> {
let pool = self.pool().await?;
let app = App::from(pool).await?;
@@ -66,10 +111,15 @@ fn started_msg(listener: &net::TcpListener) -> io::Result<String> {
Ok(format!("listening on http://{local_addr}/"))
}
+/// Errors that can be raised by [`Args::run`].
#[derive(Debug, thiserror::Error)]
#[error(transparent)]
pub enum Error {
+ /// Failure due to `io::Error`. See [`io::Error`].
IoError(#[from] io::Error),
+ /// Failure due to a database error. See [`sqlx::Error`].
DatabaseError(#[from] sqlx::Error),
+ /// Failure due to a database migration error. See
+ /// [`sqlx::migrate::MigrateError`].
MigrateError(#[from] sqlx::migrate::MigrateError),
}
diff --git a/src/clock.rs b/src/clock.rs
index d162fc0..81ecede 100644
--- a/src/clock.rs
+++ b/src/clock.rs
@@ -8,10 +8,10 @@ use chrono::Utc;
pub type DateTime = chrono::DateTime<chrono::Utc>;
-/// Extractor that provides the "current time" for a request. This time is calculated
-/// once per request, even if the extractor is used in multiple places. This requires
-/// the [middleware] function to be installed with [axum::middleware::from_fn] around
-/// the current route.
+// Extractor that provides the "current time" for a request. This time is calculated
+// once per request, even if the extractor is used in multiple places. This requires
+// the [middleware] function to be installed with [axum::middleware::from_fn] around
+// the current route.
#[derive(Clone)]
pub struct RequestedAt(pub DateTime);
@@ -53,8 +53,8 @@ impl std::ops::Deref for RequestedAt {
}
}
-/// Computes a canonical "requested at" time for each request it wraps. This
-/// time can be recovered using the [RequestedAt] extractor.
+// Computes a canonical "requested at" time for each request it wraps. This
+// time can be recovered using the [RequestedAt] extractor.
pub async fn middleware(mut req: Request, next: Next) -> Result<Response, StatusCode> {
let now = RequestedAt::now();
req.extensions_mut().insert(now);
diff --git a/src/error.rs b/src/error.rs
index e2128d3..ffc90e9 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -40,7 +40,7 @@ impl IntoResponse for InternalError {
}
}
-/// Transient identifier for an InternalError. Prefixed with `E`.
+// Transient identifier for an InternalError. Prefixed with `E`.
#[derive(Debug)]
pub struct Id(BaseId);
diff --git a/src/events/extract.rs b/src/events/extract.rs
index 683c1f9..44bcc71 100644
--- a/src/events/extract.rs
+++ b/src/events/extract.rs
@@ -7,8 +7,8 @@ use axum::{
use axum_extra::typed_header::TypedHeader;
use serde::{de::DeserializeOwned, Serialize};
-/// A typed header. When used as a bare extractor, reads from the
-/// `Last-Event-Id` HTTP header.
+// A typed header. When used as a bare extractor, reads from the
+// `Last-Event-Id` HTTP header.
pub struct LastEventId<T>(pub T);
static LAST_EVENT_ID: HeaderName = HeaderName::from_static("last-event-id");
diff --git a/src/lib.rs b/src/lib.rs
index a7ca18b..9071e51 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,3 +1,5 @@
+//! `hi` - a web-based, self-hosted chat system.
+
mod app;
mod channel;
pub mod cli;
diff --git a/src/login/extract.rs b/src/login/extract.rs
index bda55cd..542f879 100644
--- a/src/login/extract.rs
+++ b/src/login/extract.rs
@@ -13,7 +13,7 @@ pub struct IdentityToken {
}
impl IdentityToken {
- /// Creates a new, unpopulated identity token store.
+ // Creates a new, unpopulated identity token store.
#[cfg(test)]
pub fn new() -> Self {
Self {
@@ -21,17 +21,17 @@ impl IdentityToken {
}
}
- /// Get the identity secret sent in the request, if any. If the identity
- /// was not sent, or if it has previously been [clear]ed, then this will
- /// return [None]. If the identity has previously been [set], then this
- /// will return that secret, regardless of what the request originally
- /// included.
+ // Get the identity secret sent in the request, if any. If the identity
+ // was not sent, or if it has previously been [clear]ed, then this will
+ // return [None]. If the identity has previously been [set], then this
+ // will return that secret, regardless of what the request originally
+ // included.
pub fn secret(&self) -> Option<&str> {
self.cookies.get(IDENTITY_COOKIE).map(Cookie::value)
}
- /// Positively set the identity secret, and ensure that it will be sent
- /// back to the client when this extractor is included in a response.
+ // Positively set the identity secret, and ensure that it will be sent
+ // back to the client when this extractor is included in a response.
pub fn set(self, secret: &str) -> Self {
let identity_cookie = Cookie::build((IDENTITY_COOKIE, String::from(secret)))
.http_only(true)
@@ -43,8 +43,8 @@ impl IdentityToken {
}
}
- /// Remove the identity secret and ensure that it will be cleared when this
- /// extractor is included in a response.
+ // Remove the identity secret and ensure that it will be cleared when this
+ // extractor is included in a response.
pub fn clear(self) -> Self {
Self {
cookies: self.cookies.remove(IDENTITY_COOKIE),
diff --git a/src/login/repo/auth.rs b/src/login/repo/auth.rs
index 78b44f0..3033c8f 100644
--- a/src/login/repo/auth.rs
+++ b/src/login/repo/auth.rs
@@ -18,9 +18,9 @@ impl<'c> Provider for Transaction<'c, Sqlite> {
pub struct Auth<'t>(&'t mut SqliteConnection);
impl<'t> Auth<'t> {
- /// Retrieves a login by name, plus its stored password hash for
- /// verification. If there's no login with the requested name, this will
- /// return [None].
+ // Retrieves a login by name, plus its stored password hash for
+ // verification. If there's no login with the requested name, this will
+ // return [None].
pub async fn for_name(
&mut self,
name: &str,
diff --git a/src/main.rs b/src/main.rs
index d199a74..d0830ff 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3,7 +3,7 @@ use clap::Parser;
use hi::cli;
#[tokio::main]
-async fn main() -> cli::Result<()> {
+async fn main() -> Result<(), cli::Error> {
let args = cli::Args::parse();
args.run().await
}
diff --git a/src/repo/channel.rs b/src/repo/channel.rs
index da63b45..0186413 100644
--- a/src/repo/channel.rs
+++ b/src/repo/channel.rs
@@ -23,7 +23,6 @@ pub struct Channel {
}
impl<'c> Channels<'c> {
- /// Create a new channel.
pub async fn create(&mut self, name: &str) -> Result<Channel, sqlx::Error> {
let id = Id::generate();
@@ -78,7 +77,7 @@ impl<'c> Channels<'c> {
}
}
-/// Stable identifier for a [Channel]. Prefixed with `C`.
+// Stable identifier for a [Channel]. Prefixed with `C`.
#[derive(
Clone,
Debug,
diff --git a/src/repo/login/store.rs b/src/repo/login/store.rs
index 2f922d7..b485941 100644
--- a/src/repo/login/store.rs
+++ b/src/repo/login/store.rs
@@ -62,7 +62,7 @@ impl<'t> From<&'t mut SqliteConnection> for Logins<'t> {
}
}
-/// Stable identifier for a [Login]. Prefixed with `L`.
+// Stable identifier for a [Login]. Prefixed with `L`.
#[derive(Clone, Debug, Eq, PartialEq, sqlx::Type, serde::Serialize)]
#[sqlx(transparent)]
pub struct Id(BaseId);
diff --git a/src/repo/message.rs b/src/repo/message.rs
index e331a4e..385b103 100644
--- a/src/repo/message.rs
+++ b/src/repo/message.rs
@@ -2,7 +2,7 @@ use std::fmt;
use crate::id::Id as BaseId;
-/// Stable identifier for a [Message]. Prefixed with `M`.
+// Stable identifier for a [Message]. Prefixed with `M`.
#[derive(Clone, Debug, Eq, Hash, PartialEq, sqlx::Type, serde::Deserialize, serde::Serialize)]
#[sqlx(transparent)]
#[serde(transparent)]
diff --git a/src/repo/token.rs b/src/repo/token.rs
index 4eaa6ea..8276bea 100644
--- a/src/repo/token.rs
+++ b/src/repo/token.rs
@@ -17,8 +17,8 @@ impl<'c> Provider for Transaction<'c, Sqlite> {
pub struct Tokens<'t>(&'t mut SqliteConnection);
impl<'c> Tokens<'c> {
- /// Issue a new token for an existing login. The issued_at timestamp will
- /// be used to control expiry, until the token is actually used.
+ // Issue a new token for an existing login. The issued_at timestamp will
+ // be used to control expiry, until the token is actually used.
pub async fn issue(
&mut self,
login: &Login,
@@ -43,7 +43,7 @@ impl<'c> Tokens<'c> {
Ok(secret)
}
- /// Revoke a token by its secret.
+ // Revoke a token by its secret.
pub async fn revoke(&mut self, secret: &str) -> Result<(), sqlx::Error> {
sqlx::query!(
r#"
@@ -60,8 +60,8 @@ impl<'c> Tokens<'c> {
Ok(())
}
- /// Expire and delete all tokens that haven't been used more recently than
- /// ``expire_at``.
+ // Expire and delete all tokens that haven't been used more recently than
+ // `expire_at`.
pub async fn expire(&mut self, expire_at: &DateTime) -> Result<(), sqlx::Error> {
sqlx::query!(
r#"
@@ -77,9 +77,9 @@ impl<'c> Tokens<'c> {
Ok(())
}
- /// Validate a token by its secret, retrieving the associated Login record.
- /// Will return [None] if the token is not valid. The token's last-used
- /// timestamp will be set to `used_at`.
+ // Validate a token by its secret, retrieving the associated Login record.
+ // Will return [None] if the token is not valid. The token's last-used
+ // timestamp will be set to `used_at`.
pub async fn validate(
&mut self,
secret: &str,