#![cfg_attr(
feature = "unstable-error-generic-member-access",
feature(error_generic_member_access)
)]
#![allow(
unused,
clippy::all,
reason = "derive-macro test fixtures intentionally trip style lints"
)]
use oopsie::Oopsie;
#[derive(Debug, Oopsie)]
#[oopsie(module)]
enum MyAppError {
#[oopsie("not found: {name}")]
NotFound { name: String },
#[oopsie("timed out")]
TimedOut,
}
#[test]
fn default_module_name() {
let err = my_app_oopsies::NotFound { name: "widget" }.build();
assert!(matches!(err, MyAppError::NotFound { .. }));
assert_eq!(err.to_string(), "not found: widget");
let err = my_app_oopsies::TimedOut.build();
assert!(matches!(err, MyAppError::TimedOut));
assert_eq!(err.to_string(), "timed out");
}
#[derive(Debug, Oopsie)]
#[oopsie(module(custom_mod))]
enum ServiceError {
#[oopsie("unavailable: {reason}")]
Unavailable { reason: String },
#[oopsie("rate limited")]
RateLimited,
}
#[test]
fn custom_module_name() {
let err = custom_mod::Unavailable {
reason: "maintenance",
}
.build();
assert!(matches!(err, ServiceError::Unavailable { .. }));
assert_eq!(err.to_string(), "unavailable: maintenance");
let err = custom_mod::RateLimited.build();
assert!(matches!(err, ServiceError::RateLimited));
assert_eq!(err.to_string(), "rate limited");
}
#[derive(Debug, Oopsie)]
#[oopsie(module(false))]
enum FlatError {
#[oopsie("bad input: {detail}")]
BadInput { detail: String },
#[oopsie("internal")]
Internal,
}
#[test]
fn module_false_no_wrapping() {
let err = BadInput {
detail: "negative value",
}
.build();
assert!(matches!(err, FlatError::BadInput { .. }));
assert_eq!(err.to_string(), "bad input: negative value");
let err = Internal.build();
assert!(matches!(err, FlatError::Internal));
assert_eq!(err.to_string(), "internal");
}
#[derive(Debug, Oopsie)]
enum AppError {
#[oopsie("connection failed")]
Connect,
#[oopsie("disk full: {bytes}")]
DiskFull { bytes: u64 },
}
#[test]
fn bare_enum_default_auto_module() {
let err = app_oopsies::Connect.build();
assert!(matches!(err, AppError::Connect));
assert_eq!(err.to_string(), "connection failed");
let err = app_oopsies::DiskFull { bytes: 4096u64 }.build();
assert!(matches!(err, AppError::DiskFull { bytes: 4096 }));
assert_eq!(err.to_string(), "disk full: 4096");
}
#[derive(Debug, Oopsie)]
#[oopsie(module)]
enum Error {
#[oopsie("generic error")]
Generic,
#[oopsie("specific: {what}")]
Specific { what: String },
}
#[test]
fn enum_named_error_module_is_oopsies() {
let err = oopsies::Generic.build();
assert!(matches!(err, Error::Generic));
assert_eq!(err.to_string(), "generic error");
let err = oopsies::Specific { what: "boom" }.build();
assert!(matches!(err, Error::Specific { .. }));
assert_eq!(err.to_string(), "specific: boom");
}
#[derive(Debug, Oopsie)]
#[oopsie(module(query_oopsies))]
#[oopsie("query failed: {what}")]
pub struct QueryError {
what: String,
}
#[test]
fn struct_module_wraps_selector() {
let err = query_oopsies::QueryOopsie { what: "join" }.build();
assert_eq!(err.to_string(), "query failed: join");
}
#[derive(Debug, Oopsie)]
#[oopsie(module)]
#[oopsie("parse failed")]
pub struct ParseError;
#[test]
fn struct_auto_module_name() {
let err = parse_oopsies::ParseOopsie.build();
assert_eq!(err.to_string(), "parse failed");
}
#[derive(Debug, Oopsie)]
#[oopsie("flat struct error")]
pub struct FlatStructError;
#[test]
fn struct_default_no_module() {
let err = FlatStructOopsie.build();
assert_eq!(err.to_string(), "flat struct error");
}
mod crate_vis {
#[expect(
clippy::redundant_pub_crate,
reason = "pub(crate) is the point: it exercises the restricted-visibility lift path"
)]
#[derive(Debug, oopsie::Oopsie)]
#[oopsie(module(scoped_oopsies))]
#[oopsie("scoped: {what}")]
pub(crate) struct ScopedError {
pub(crate) what: String,
}
}
#[test]
fn restricted_vis_struct_module_selector_reachable_crate_wide() {
let err = crate_vis::scoped_oopsies::ScopedOopsie { what: "x" }.build();
assert_eq!(err.to_string(), "scoped: x");
}
#[derive(Debug, Oopsie)]
#[oopsie(module(test_oopsies))]
pub enum MixedModuleVisError {
#[oopsie("private variant")]
PrivateVariant { detail: String },
#[oopsie("public variant")]
#[oopsie(vis(pub))]
PublicVariant { code: i32 },
}
pub use test_oopsies::PublicVariant;
#[test]
fn module_wrapping_with_variant_vis_override() {
let err = test_oopsies::PrivateVariant { detail: "secret" }.build();
assert!(matches!(err, MixedModuleVisError::PrivateVariant { .. }));
assert_eq!(err.to_string(), "private variant");
let err = test_oopsies::PublicVariant { code: 7i32 }.build();
assert!(matches!(
err,
MixedModuleVisError::PublicVariant { code: 7 }
));
assert_eq!(err.to_string(), "public variant");
let err = PublicVariant { code: 42i32 }.build();
assert!(matches!(
err,
MixedModuleVisError::PublicVariant { code: 42 }
));
}