use uuid::Uuid;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum InventoryError {
#[error("inventory item not found: {0}")]
ItemNotFound(String),
#[error("insufficient stock for {sku}: requested {requested}, available {available}")]
InsufficientStock {
sku: String,
requested: String,
available: String,
},
#[error("reservation not found: {0}")]
ReservationNotFound(Uuid),
#[error("reservation expired: {0}")]
ReservationExpired(Uuid),
#[error("duplicate SKU: {0}")]
DuplicateSku(String),
#[error(transparent)]
Other(Box<dyn std::error::Error + Send + Sync>),
}
impl InventoryError {
#[track_caller]
pub fn item_not_found(id: impl Into<String>) -> Self {
Self::ItemNotFound(id.into())
}
#[track_caller]
pub fn insufficient_stock(
sku: impl Into<String>,
requested: impl Into<String>,
available: impl Into<String>,
) -> Self {
Self::InsufficientStock {
sku: sku.into(),
requested: requested.into(),
available: available.into(),
}
}
#[inline]
#[track_caller]
#[must_use]
pub const fn reservation_not_found(id: Uuid) -> Self {
Self::ReservationNotFound(id)
}
#[inline]
#[track_caller]
#[must_use]
pub const fn reservation_expired(id: Uuid) -> Self {
Self::ReservationExpired(id)
}
#[track_caller]
pub fn duplicate_sku(sku: impl Into<String>) -> Self {
Self::DuplicateSku(sku.into())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn item_not_found_display() {
let err = InventoryError::item_not_found("SKU-999");
assert_eq!(err.to_string(), "inventory item not found: SKU-999");
}
#[test]
fn insufficient_stock_display() {
let err = InventoryError::insufficient_stock("WIDGET", "10", "3");
let msg = err.to_string();
assert!(msg.contains("WIDGET"));
assert!(msg.contains("10"));
assert!(msg.contains("3"));
}
#[test]
fn reservation_not_found_display() {
let id = Uuid::nil();
let err = InventoryError::reservation_not_found(id);
assert!(err.to_string().contains(&id.to_string()));
}
#[test]
fn converts_to_commerce_error() {
use crate::errors::CommerceError;
let inv_err = InventoryError::item_not_found("SKU-001");
let commerce_err: CommerceError = inv_err.into();
assert!(commerce_err.is_not_found());
}
#[test]
fn duplicate_sku_display() {
let err = InventoryError::duplicate_sku("DUPE-001");
assert_eq!(err.to_string(), "duplicate SKU: DUPE-001");
}
}