use diapause::{Coroutine, CoroutineState};
#[derive(Debug, PartialEq)]
pub struct ParseError;
fn parse(s: &str) -> Result<u32, ParseError> {
s.parse().map_err(|_| ParseError)
}
#[diapause::coroutine(yield = u32)]
fn sum_two(a: &'static str, b: &'static str) -> Result<u32, ParseError> {
let x: u32 = parse(a)?;
yield_!(x);
let y: u32 = parse(b)?;
Ok(x + y)
}
#[test]
fn result_success_path() {
let mut c = sum_two("1", "2");
assert_eq!(c.start(), CoroutineState::Yielded(1));
assert_eq!(c.resume(()), CoroutineState::Complete(Ok(3)));
}
#[test]
fn result_failure_before_first_yield() {
let mut c = sum_two("x", "2");
assert_eq!(c.start(), CoroutineState::Complete(Err(ParseError)));
}
#[test]
fn result_failure_after_resume() {
let mut c = sum_two("1", "x");
assert_eq!(c.start(), CoroutineState::Yielded(1));
assert_eq!(c.resume(()), CoroutineState::Complete(Err(ParseError)));
}
#[test]
#[should_panic(expected = "Already done")]
fn resume_after_try_exit_panics() {
let mut c = sum_two("x", "2");
let _ = c.start();
let _ = c.resume(());
}
#[derive(Debug, PartialEq)]
pub struct WrappedError(&'static str);
impl From<ParseError> for WrappedError {
fn from(_: ParseError) -> Self {
WrappedError("bad number")
}
}
#[diapause::coroutine(yield = u32)]
fn converts_error(s: &'static str) -> Result<u32, WrappedError> {
let x: u32 = parse(s)?;
yield_!(x);
Ok(x)
}
#[test]
fn result_from_conversion() {
let mut c = converts_error("nope");
assert_eq!(
c.start(),
CoroutineState::Complete(Err(WrappedError("bad number")))
);
}
#[diapause::coroutine(yield = u32, resume = u32)]
fn option_try(start: Option<u32>) -> Option<u32> {
let x: u32 = start?;
let r = yield_!(x);
Some(x.checked_add(r)?)
}
#[test]
fn option_success_path() {
let mut c = option_try(Some(4));
assert_eq!(c.start(), CoroutineState::Yielded(4));
assert_eq!(c.resume(1), CoroutineState::Complete(Some(5)));
}
#[test]
fn option_none_short_circuits() {
let mut c = option_try(None);
assert_eq!(c.start(), CoroutineState::Complete(None));
}
#[test]
fn option_none_in_yield_value() {
let mut c = option_try(Some(u32::MAX));
assert_eq!(c.start(), CoroutineState::Yielded(u32::MAX));
assert_eq!(c.resume(1), CoroutineState::Complete(None));
}
#[diapause::coroutine(yield = u32, resume = &'static str)]
fn try_yield_try(a: &'static str) -> Result<u32, ParseError> {
let x: u32 = parse(a)?;
let b = yield_!(x);
let y: u32 = parse(b)?;
Ok(x * 100 + y)
}
#[test]
fn try_then_yield_then_try() {
let mut c = try_yield_try("7");
assert_eq!(c.start(), CoroutineState::Yielded(7));
assert_eq!(c.resume("42"), CoroutineState::Complete(Ok(742)));
let mut c = try_yield_try("7");
assert_eq!(c.start(), CoroutineState::Yielded(7));
assert_eq!(c.resume("x"), CoroutineState::Complete(Err(ParseError)));
}
#[diapause::coroutine(yield = u32)]
fn try_in_opaque_if(s: &'static str, check: bool) -> Result<u32, ParseError> {
let mut x: u32 = 1;
if check {
x += parse(s)?;
} else {
x += 100;
}
yield_!(x);
Ok(x)
}
#[test]
fn try_inside_opaque_if() {
let mut c = try_in_opaque_if("9", true);
assert_eq!(c.start(), CoroutineState::Yielded(10));
assert_eq!(c.resume(()), CoroutineState::Complete(Ok(10)));
let mut c = try_in_opaque_if("x", true);
assert_eq!(c.start(), CoroutineState::Complete(Err(ParseError)));
let mut c = try_in_opaque_if("x", false);
assert_eq!(c.start(), CoroutineState::Yielded(101));
}
#[diapause::coroutine(yield = u32)]
fn try_in_yield_value(s: &'static str) -> Result<u32, ParseError> {
yield_!(parse(s)?);
Ok(0)
}
#[test]
fn try_inside_yield_value() {
let mut c = try_in_yield_value("3");
assert_eq!(c.start(), CoroutineState::Yielded(3));
assert_eq!(c.resume(()), CoroutineState::Complete(Ok(0)));
let mut c = try_in_yield_value("x");
assert_eq!(c.start(), CoroutineState::Complete(Err(ParseError)));
}
#[diapause::coroutine(yield = u32)]
fn nested_try(a: Option<&'static str>) -> Result<u32, ParseError> {
let x: u32 = parse(a.ok_or(ParseError)?)?;
yield_!(x);
Ok(x)
}
#[test]
fn nested_try_operands() {
let mut c = nested_try(Some("5"));
assert_eq!(c.start(), CoroutineState::Yielded(5));
let mut c = nested_try(None);
assert_eq!(c.start(), CoroutineState::Complete(Err(ParseError)));
let mut c = nested_try(Some("x"));
assert_eq!(c.start(), CoroutineState::Complete(Err(ParseError)));
}
#[diapause::coroutine(yield = u32)]
fn closure_try(s: &'static str) -> u32 {
let double = |s: &str| -> Result<u32, ParseError> { Ok(parse(s)? * 2) };
let x: u32 = double(s).unwrap_or(0);
yield_!(x);
x
}
#[test]
fn try_inside_closure_is_untouched() {
let mut c = closure_try("21");
assert_eq!(c.start(), CoroutineState::Yielded(42));
assert_eq!(c.resume(()), CoroutineState::Complete(42));
let mut c = closure_try("x");
assert_eq!(c.start(), CoroutineState::Yielded(0));
}