Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## Unreleased

### Changed

- Use NonZero internally for data where 0 is not a valid value and
any special meaning in expressed via an outer Option.


## 2.5.0

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ run-time behavior:
* `WASTEBIN_MAX_BODY_SIZE` number of bytes to accept for POST requests. Defaults
to 1 MB.
* `WASTEBIN_MAX_PASTE_EXPIRATION` maximum allowed lifetime of a paste in
seconds. Defaults to unlimited.
seconds. Defaults to 0 meaning unlimited.
* `WASTEBIN_PASSWORD_SALT` salt used to hash user passwords used for encrypting
pastes.
* `WASTEBIN_SIGNING_KEY` sets the key to sign cookies. If not set, a random key
Expand Down
7 changes: 5 additions & 2 deletions src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ pub mod write {
use async_compression::tokio::bufread::ZstdEncoder;
use serde::{Deserialize, Serialize};
use std::io::Cursor;
use std::num::NonZeroU32;
use tokio::io::{AsyncReadExt, BufReader};

/// An uncompressed entry to be inserted into the database.
Expand All @@ -78,7 +79,7 @@ pub mod write {
/// File extension
pub extension: Option<String>,
/// Expiration in seconds from now
pub expires: Option<u32>,
pub expires: Option<NonZeroU32>,
/// Delete if read
pub burn_after_reading: Option<bool>,
/// User identifier that inserted the entry
Expand Down Expand Up @@ -364,6 +365,8 @@ impl Database {

#[cfg(test)]
mod tests {
use std::num::NonZero;

use super::*;

fn new_db() -> Result<Database, Box<dyn std::error::Error>> {
Expand Down Expand Up @@ -399,7 +402,7 @@ mod tests {
let db = new_db()?;

let entry = write::Entry {
expires: Some(1),
expires: Some(NonZero::new(1).unwrap()),
..Default::default()
};

Expand Down
5 changes: 3 additions & 2 deletions src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{db, highlight};
use axum_extra::extract::cookie::Key;
use std::env::VarError;
use std::net::SocketAddr;
use std::num::{NonZeroUsize, ParseIntError};
use std::num::{NonZero, NonZeroU32, NonZeroUsize, ParseIntError};
use std::path::PathBuf;
use std::sync::LazyLock;
use std::time::Duration;
Expand Down Expand Up @@ -178,9 +178,10 @@ pub fn http_timeout() -> Result<Duration, Error> {
.map_err(Error::HttpTimeout)
}

pub fn max_paste_expiration() -> Result<Option<u32>, Error> {
pub fn max_paste_expiration() -> Result<Option<NonZeroU32>, Error> {
std::env::var(VAR_MAX_PASTE_EXPIRATION)
.ok()
.map(|value| value.parse::<u32>().map_err(Error::MaxPasteExpiration))
.transpose()
.map(|op| op.and_then(NonZero::new))
}
3 changes: 2 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::errors::Error;
use axum::extract::{DefaultBodyLimit, FromRef};
use axum::Router;
use axum_extra::extract::cookie::Key;
use std::num::NonZeroU32;
use std::process::ExitCode;
use std::time::Duration;
use tokio::net::TcpListener;
Expand Down Expand Up @@ -32,7 +33,7 @@ pub struct AppState {
cache: Cache,
key: Key,
base_url: Option<Url>,
max_expiration: Option<u32>,
max_expiration: Option<NonZeroU32>,
}

impl FromRef<AppState> for Key {
Expand Down
31 changes: 22 additions & 9 deletions src/pages.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use std::num::NonZero;
use std::num::NonZeroU32;

use crate::cache::Key as CacheKey;
use crate::env;
use crate::highlight::Html;
Expand Down Expand Up @@ -35,11 +38,11 @@ impl From<crate::Error> for ErrorResponse<'_> {
pub struct Index<'a> {
meta: &'a env::Metadata<'a>,
base_path: &'static env::BasePath,
max_expiration: Option<u32>,
max_expiration: Option<NonZeroU32>,
}

impl<'a> Index<'a> {
pub fn new(max_expiration: Option<u32>) -> Self {
pub fn new(max_expiration: Option<NonZeroU32>) -> Self {
Self {
meta: &env::METADATA,
base_path: &env::BASE_PATH,
Expand All @@ -52,7 +55,7 @@ impl<'a> Index<'a> {
enum Expiration {
None,
Burn,
Time(u32),
Time(NonZeroU32),
}

impl std::fmt::Display for Expiration {
Expand All @@ -65,14 +68,24 @@ impl std::fmt::Display for Expiration {
}
}

// TODO: replace once Option::expect is const (https://github.com/rust-lang/rust/issues/67441) to construct EXPIRATION_OPTIONS
macro_rules! nonzero {
($value:literal) => {
match NonZero::new($value) {
Some(v) => v,
None => unreachable!(),
}
};
}

const EXPIRATION_OPTIONS: [(&str, Expiration); 8] = [
("never", Expiration::None),
("10 minutes", Expiration::Time(600)),
("1 hour", Expiration::Time(3600)),
("1 day", Expiration::Time(86400)),
("1 week", Expiration::Time(604_800)),
("1 month", Expiration::Time(2_592_000)),
("1 year", Expiration::Time(31_536_000)),
("10 minutes", Expiration::Time(nonzero!(600))),
("1 hour", Expiration::Time(nonzero!(3600))),
("1 day", Expiration::Time(nonzero!(86400))),
("1 week", Expiration::Time(nonzero!(604_800))),
("1 month", Expiration::Time(nonzero!(2_592_000))),
("1 year", Expiration::Time(nonzero!(31_536_000))),
("🔥 after reading", Expiration::Burn),
];

Expand Down
6 changes: 4 additions & 2 deletions src/routes/form.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::num::NonZeroU32;

use crate::db::write;
use crate::env::BASE_PATH;
use crate::id::Id;
Expand All @@ -21,8 +23,8 @@ impl From<Entry> for write::Entry {
let burn_after_reading = Some(entry.expires == "burn");
let password = (!entry.password.is_empty()).then_some(entry.password);

let expires = match entry.expires.parse::<u32>() {
Ok(0) | Err(_) => None,
let expires = match entry.expires.parse::<NonZeroU32>() {
Err(_) => None,
Ok(secs) => Some(secs),
};

Expand Down
4 changes: 3 additions & 1 deletion src/routes/json.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::num::NonZeroU32;

use crate::db::write;
use crate::env::BASE_PATH;
use crate::errors::{Error, JsonErrorResponse};
Expand All @@ -12,7 +14,7 @@ use serde::{Deserialize, Serialize};
pub struct Entry {
pub text: String,
pub extension: Option<String>,
pub expires: Option<u32>,
pub expires: Option<NonZeroU32>,
pub burn_after_reading: Option<bool>,
pub password: Option<String>,
}
Expand Down