Skip to content

Instantly share code, notes, and snippets.

Created Sep 16, 2017
Embed
What would you like to do?
Rust code shared from the playground
use std::error::Error;
use std::fmt;
/// Non-critical shopping error (warning).
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ShoppingWarning {
Dirty(u8),
Broken,
}
impl fmt::Display for ShoppingWarning {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ShoppingWarning::Dirty(level) => {
write!(f, "Item is dirty (level={})", level)
},
_ => write!(f, "{}", self.description()),
}
}
}
impl Error for ShoppingWarning {
fn description(&self) -> &str {
match *self {
ShoppingWarning::Dirty(_) => "Item is dirty",
ShoppingWarning::Broken => "Item is broken",
}
}
}
/// (Potentially) critical shopping error.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ShoppingError {
SoldOut,
ShopClosed,
NotEnoughMoney,
/// Treat warning as critical.
Warning(ShoppingWarning),
}
impl fmt::Display for ShoppingError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ShoppingError::Warning(ref w) => w.fmt(f),
_ => write!(f, "{}", self.description()),
}
}
}
impl Error for ShoppingError {
fn description(&self) -> &str {
match *self {
ShoppingError::SoldOut => "Sold out, no items",
ShoppingError::ShopClosed => "Shop is closed",
ShoppingError::NotEnoughMoney => "You should have more money",
ShoppingError::Warning(ref w) => w.description(),
}
}
fn cause(&self) -> Option<&Error> {
match *self {
ShoppingError::Warning(ref w) => Some(w),
_ => None,
}
}
}
impl From<ShoppingWarning> for ShoppingError {
fn from(w: ShoppingWarning) -> Self {
ShoppingError::Warning(w)
}
}
fn buy<F>(item_id: u32, amount: usize, mut warning_handler: F)
-> Result<usize, ShoppingError>
where
F: FnMut(ShoppingWarning) -> Result<(), ShoppingWarning>,
{
buy_impl(item_id, amount, &mut warning_handler)
}
/// Attempts to buy `amount`-number of items indicated by `item_id`.
///
/// * 0: sold out
/// * 1: 1 item and 1 dirty (level 2) item.
/// * 2: 1 dirty item (level 5) and 1 broken item.
fn buy_impl<F>(item_id: u32, amount: usize, warning_handler: &mut F)
-> Result<usize, ShoppingError>
where
F: FnMut(ShoppingWarning) -> Result<(), ShoppingWarning>,
{
match item_id {
0 => {
if amount == 0 {
Ok(0)
} else {
Err(ShoppingError::SoldOut)
}
},
1 => {
match amount {
0 | 1 => Ok(0),
2 => {
warning_handler(ShoppingWarning::Dirty(2))?;
Ok(2)
},
_ => Err(ShoppingError::SoldOut),
}
},
2 => {
if amount >= 3 {
return Err(ShoppingError::SoldOut);
}
if amount >= 1 {
warning_handler(ShoppingWarning::Dirty(5))?;
if amount >= 2 {
warning_handler(ShoppingWarning::Broken)?;
}
}
Ok(amount)
},
_ => Err(ShoppingError::SoldOut),
}
}
fn main() {
// Buy any items.
assert_eq!(
Err(ShoppingError::SoldOut),
buy(0, 1, |_| Ok(()))
);
// Buy only clean and not broken items.
assert_eq!(
Err(ShoppingError::Warning(ShoppingWarning::Dirty(2))),
buy(1, 2, |w| Err(w))
);
// Buy not broken items, allow dirty items.
assert_eq!(
Ok(2),
buy(1, 2, |w| {
if let ShoppingWarning::Broken = w {
Err(w)
} else {
Ok(())
}
})
);
// Buy not broken items, allow dirty items.
assert_eq!(
Err(ShoppingError::Warning(ShoppingWarning::Broken)),
buy(2, 2, |w| {
if let ShoppingWarning::Broken = w {
Err(w)
} else {
Ok(())
}
})
);
// Buy not broken items, allow dirty items.
// Log troubles.
let mut troubles = Vec::new();
assert_eq!(
Err(ShoppingError::Warning(ShoppingWarning::Broken)),
buy(2, 2, |w| {
troubles.push(w.clone());
if let ShoppingWarning::Broken = w {
Err(w)
} else {
Ok(())
}
})
);
assert_eq!(
troubles,
vec![
ShoppingWarning::Dirty(5),
ShoppingWarning::Broken,
]
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment