use raskell::hdo;
#[derive(Debug, PartialEq)]
enum UserError {
Inactive,
PatternMismatch,
}
#[test]
fn option_bind() {
let result = hdo! {
x <- Some(10);
y <- Some(20);
pure x + y
};
assert_eq!(result, Some(30));
}
#[test]
fn option_short_circuits_on_none() {
let result = hdo! {
x <- Some(10);
y <- None::<i32>;
pure x + y
};
assert_eq!(result, None);
}
#[test]
fn let_bindings_work() {
let result = hdo! {
x <- Some(10);
let y = x * 2;
let z = y + 5;
pure z
};
assert_eq!(result, Some(25));
}
#[test]
fn option_guard_passes() {
let result = hdo! {
x <- Some(10);
guard x > 5;
pure x * 2
};
assert_eq!(result, Some(20));
}
#[test]
fn option_guard_stops() {
let result = hdo! {
x <- Some(10);
guard x > 50;
pure x * 2
};
assert_eq!(result, None);
}
#[test]
fn result_bind() {
let result: Result<i32, UserError> = hdo! {
x <- Ok(10);
y <- Ok(20);
pure x + y
};
assert_eq!(result, Ok(30));
}
#[test]
fn result_guard_passes() {
let result: Result<i32, UserError> = hdo! {
x <- Ok(10);
guard x > 5 throw UserError::Inactive;
pure x * 2
};
assert_eq!(result, Ok(20));
}
#[test]
fn result_guard_throws() {
let result: Result<i32, UserError> = hdo! {
x <- Ok(10);
guard x > 50 throw UserError::Inactive;
pure x * 2
};
assert_eq!(result, Err(UserError::Inactive));
}
#[test]
fn ordinary_rust_statements_work() {
let result = hdo! {
x <- Some(10);
let mut y = x;
y += 5;
pure y
};
assert_eq!(result, Some(15));
}
#[derive(Debug, PartialEq)]
enum ActionError {
Boom,
}
fn fail() -> Result<(), ActionError> {
Err(ActionError::Boom)
}
#[test]
fn bare_result_action_short_circuits() {
let result: Result<i32, ActionError> = hdo! {
x <- Ok(10);
fail();
pure x
};
assert_eq!(result, Err(ActionError::Boom));
}
#[test]
fn option_refutable_pattern_matches() {
let result = hdo! {
Some(x) <- Some(Some(10));
pure x * 2
};
assert_eq!(result, Some(20));
}
#[test]
fn option_refutable_pattern_fails() {
let result = hdo! {
Some(x) <- Some(None::<i32>);
pure x * 2
};
assert_eq!(result, None);
}
#[test]
fn result_refutable_pattern_matches() {
let result: Result<i32, UserError> = hdo! {
Some(x) <- Ok(Some(10)) throw UserError::Inactive;
pure x * 2
};
assert_eq!(result, Ok(20));
}
#[test]
fn result_refutable_pattern_mismatch_throws() {
let result: Result<i32, UserError> = hdo! {
Some(x) <- Ok(None::<i32>) throw UserError::Inactive;
pure x * 2
};
assert_eq!(result, Err(UserError::Inactive));
}
#[test]
fn result_refutable_pattern_preserves_error() {
let failed: Result<Option<i32>, UserError> = Err(UserError::Inactive);
let result = hdo! {
Some(x) <- failed throw UserError::PatternMismatch;
pure x * 2
};
assert_eq!(result, Err(UserError::Inactive));
}
#[test]
fn bind_type_ascription_pins_untyped_expression() {
let result: Result<i32, UserError> = hdo! {
Some(x): Option<i32> <- Err(UserError::Inactive) throw UserError::PatternMismatch;
pure x * 2
};
assert_eq!(result, Err(UserError::Inactive));
}
#[test]
fn bind_type_ascription_on_irrefutable_pattern() {
let result = hdo! {
x: u8 <- Some(10);
pure x * 2
};
assert_eq!(result, Some(20u8));
}
#[test]
fn bind_type_ascription_on_option_pattern() {
let result = hdo! {
Some(x): Option<u8> <- Some(Some(10));
pure x * 2
};
assert_eq!(result, Some(20u8));
}
#[test]
fn let_type_ascription_works() {
let result = hdo! {
x <- Some(10);
let y: i64 = i64::from(x) * 2;
pure y
};
assert_eq!(result, Some(20i64));
}
#[test]
fn bind_type_ascription_with_nested_generics() {
let result: Result<usize, UserError> = hdo! {
Some(items): Option<Vec<Result<i32, UserError>>>
<- Ok(Some(vec![Ok(1), Err(UserError::Inactive)]))
throw UserError::PatternMismatch;
pure items.len()
};
assert_eq!(result, Ok(2));
}
#[test]
fn bind_type_ascription_on_reference() {
let value = 10i32;
let result = hdo! {
x: &i32 <- Some(&value);
pure *x * 2
};
assert_eq!(result, Some(20));
}
#[test]
fn bind_type_ascription_on_tuple() {
let result = hdo! {
(a, b): (u8, u8) <- Some((1, 2));
pure a + b
};
assert_eq!(result, Some(3u8));
}
#[test]
fn multiple_typed_binds_chain() {
let result: Result<i64, UserError> = hdo! {
x: i64 <- Ok(10);
Some(y): Option<i64> <- Ok(Some(20)) throw UserError::PatternMismatch;
let z: i64 = x + y;
pure z * 2
};
assert_eq!(result, Ok(60));
}
#[test]
fn typed_bind_still_short_circuits_on_error() {
let result: Result<i32, UserError> = hdo! {
Some(x): Option<i32> <- Err(UserError::Inactive) throw UserError::PatternMismatch;
pure x * 2
};
assert_eq!(result, Err(UserError::Inactive));
}
#[test]
fn typed_bind_throws_on_pattern_mismatch() {
let result: Result<i32, UserError> = hdo! {
Some(x): Option<i32> <- Ok(None) throw UserError::PatternMismatch;
pure x * 2
};
assert_eq!(result, Err(UserError::PatternMismatch));
}
#[test]
fn typed_bind_without_ascription_still_works() {
let result = hdo! {
x <- Some(10);
Some(y): Option<i32> <- Some(Some(20));
pure x + y
};
assert_eq!(result, Some(30));
}