#![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(suffix, module(false))]
enum SuffixDefaultError {
#[oopsie("variant alpha happened")]
Alpha { value: u32 },
}
#[test]
fn default_oopsie_suffix() {
let err = AlphaOopsie { value: 42u32 }.build();
assert!(matches!(err, SuffixDefaultError::Alpha { value: 42 }));
assert_eq!(err.to_string(), "variant alpha happened");
}
#[derive(Debug, Oopsie)]
#[oopsie(suffix = "Ctx", module(false))]
enum SuffixCustomError {
#[oopsie("beta failed")]
Beta { msg: String },
}
#[test]
fn custom_suffix() {
let err = BetaCtx { msg: "oops" }.build();
assert!(matches!(err, SuffixCustomError::Beta { msg } if msg == "oops"));
}
#[derive(Debug, Oopsie)]
#[oopsie(module(false))]
enum NoSuffixError {
#[oopsie("gamma occurred")]
Gamma { code: i32 },
}
#[test]
fn no_suffix_uses_variant_name() {
let err = Gamma { code: 7i32 }.build();
assert!(matches!(err, NoSuffixError::Gamma { code: 7 }));
}
mod container_vis {
use oopsie::Oopsie;
#[derive(Debug, Oopsie)]
#[oopsie(vis(pub), module(false), suffix)]
#[expect(
unnameable_types,
reason = "pub enum in a private module: deliberately reachable via the re-exported pub selector but not nameable at pub"
)]
pub enum WidgetError {
#[oopsie("boom: {value}")]
Boom { value: u32 },
}
}
pub use container_vis::BoomOopsie;
#[test]
fn container_vis_pub_makes_selector_reexportable() {
let err = BoomOopsie { value: 9u32 }.build();
assert!(matches!(err, container_vis::WidgetError::Boom { value: 9 }));
}
mod variant_vis {
use oopsie::Oopsie;
#[derive(Debug, Oopsie)]
#[oopsie(module(false), suffix)]
#[expect(
unnameable_types,
reason = "pub enum in a private module: deliberately reachable via the re-exported pub selector but not nameable at pub"
)]
pub enum MixedError {
#[oopsie("loud: {n}")]
#[oopsie(vis(pub))]
Loud { n: u32 },
#[oopsie("quiet")]
Quiet { n: u32 },
}
}
pub use variant_vis::LoudOopsie;
#[test]
fn variant_vis_pub_overrides_container_default() {
let err = LoudOopsie { n: 3u32 }.build();
assert!(matches!(err, variant_vis::MixedError::Loud { n: 3 }));
}
#[derive(Debug, Oopsie)]
#[oopsie(module(false))]
enum NestedSuffixError {
#[oopsie("error variant: {detail}")]
Error { detail: String },
}
#[test]
fn variant_named_error_keeps_name_without_suffix() {
let err = Error {
detail: "kaboom".to_owned(),
}
.build();
assert_eq!(err.to_string(), "error variant: kaboom");
assert!(matches!(err, NestedSuffixError::Error { detail } if detail == "kaboom"));
}
mod lib_side {
#[oopsie::oopsie]
pub enum LibError {
#[oopsie("nope")]
Nope { what: String },
}
}
#[test]
fn pub_error_selectors_default_to_pub() {
let err = lib_side::lib_oopsies::Nope { what: "x" }.build();
assert_eq!(err.to_string(), "nope");
}
#[derive(Debug, Oopsie)]
#[oopsie(suffix, module(false))]
enum SuffixedErrorVariant {
#[oopsie("suffixed error: {code}")]
Error { code: i32 },
}
#[test]
fn variant_named_error_with_suffix_appends() {
let err = ErrorOopsie { code: 13i32 }.build();
assert!(matches!(err, SuffixedErrorVariant::Error { code: 13 }));
assert_eq!(err.to_string(), "suffixed error: 13");
}
mod lift_super {
mod inner {
use oopsie::Oopsie;
#[derive(Debug, Oopsie)]
#[oopsie(vis(pub(super)))]
pub(super) enum NestedError {
#[oopsie("nested: {value}")]
Boom { value: u32 },
}
}
#[test]
fn explicit_super_vis_lifted_into_module() {
let err = inner::nested_oopsies::Boom { value: 5u32 }.build();
assert!(matches!(err, inner::NestedError::Boom { value: 5 }));
assert_eq!(err.to_string(), "nested: 5");
}
}
mod lift_in_path {
mod scope {
use oopsie::Oopsie;
#[derive(Debug, Oopsie)]
#[oopsie(vis(pub(in super::super)))]
#[expect(
clippy::redundant_pub_crate,
reason = "the restricted visibility is the behavior under test"
)]
pub(in super::super) enum ScopedError {
#[oopsie("scoped: {detail}")]
Detail { detail: String },
}
}
#[test]
fn explicit_in_path_vis_lifted_into_module() {
let err = scope::scoped_oopsies::Detail { detail: "x" }.build();
assert_eq!(err.to_string(), "scoped: x");
assert!(matches!(err, scope::ScopedError::Detail { detail } if detail == "x"));
}
}
mod lift_private {
use oopsie::Oopsie;
#[derive(Debug, Oopsie)]
#[oopsie(vis())]
enum PrivateVisError {
#[oopsie("private: {n}")]
Tick { n: u8 },
}
#[test]
fn explicit_private_vis_lifted_into_module() {
let err = private_vis_oopsies::Tick { n: 1u8 }.build();
assert!(matches!(err, PrivateVisError::Tick { n: 1 }));
assert_eq!(err.to_string(), "private: 1");
}
}