use super::StateTransitionError;
use std::fmt;
use uuid::Uuid;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum OrderError {
#[error("order not found: {0}")]
NotFound(Uuid),
#[error("order cannot be cancelled in status: {status}")]
CannotCancel {
status: String,
},
#[error("order cannot be refunded: {reason}")]
CannotRefund {
reason: String,
},
#[error("{0}")]
InvalidTransition(StateTransitionError<OrderStatusLabel>),
#[error(transparent)]
Other(Box<dyn std::error::Error + Send + Sync>),
}
impl OrderError {
#[inline]
#[track_caller]
pub const fn not_found(id: Uuid) -> Self {
Self::NotFound(id)
}
#[track_caller]
pub fn cannot_cancel(status: impl fmt::Display) -> Self {
Self::CannotCancel { status: status.to_string() }
}
#[track_caller]
pub fn cannot_refund(reason: impl Into<String>) -> Self {
Self::CannotRefund { reason: reason.into() }
}
#[track_caller]
pub fn invalid_transition(from: impl fmt::Display, to: impl fmt::Display) -> Self {
Self::InvalidTransition(StateTransitionError::new(
OrderStatusLabel(from.to_string()),
OrderStatusLabel(to.to_string()),
))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct OrderStatusLabel(pub String);
impl fmt::Display for OrderStatusLabel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn not_found_display() {
let id = Uuid::nil();
let err = OrderError::not_found(id);
assert!(err.to_string().contains("not found"));
assert!(err.to_string().contains(&id.to_string()));
}
#[test]
fn cannot_cancel_display() {
let err = OrderError::cannot_cancel("shipped");
assert!(err.to_string().contains("shipped"));
}
#[test]
fn invalid_transition_display() {
let err = OrderError::invalid_transition("pending", "delivered");
let msg = err.to_string();
assert!(msg.contains("pending"));
assert!(msg.contains("delivered"));
}
#[test]
fn converts_to_commerce_error() {
use crate::errors::CommerceError;
let order_err = OrderError::not_found(Uuid::nil());
let commerce_err: CommerceError = order_err.into();
assert!(commerce_err.is_not_found());
}
#[test]
fn other_variant() {
let io_err = std::io::Error::other("disk full");
let err = OrderError::Other(Box::new(io_err));
assert!(err.to_string().contains("disk full"));
}
}