diapause 0.2.0

Coroutines/generators for stable Rust via code transformation — no async, no Pin, no allocation, no unsafe code
Documentation
//! The code examples from the transformation-summary document, verbatim
//! where possible, so the documentation and the implementation cannot
//! drift apart. Comments name the section each example comes from.

use std::fmt::Display;

use diapause::{Coroutine, CoroutineState};

// "シンプルなケース(yieldなし)"
#[diapause::coroutine]
pub fn simple() {}

#[test]
fn simple_case() {
    let mut c = simple();
    assert_eq!(c.start(), CoroutineState::Complete(()));
}

// "状態enumへのderive"
#[diapause::coroutine]
#[derive(Clone)]
fn derive_example() {}

#[test]
fn derive_on_state_enum() {
    let c = derive_example();
    let mut copy = c.clone();
    assert_eq!(copy.start(), CoroutineState::Complete(()));
}

// "ジェネリクス・参照引数"
pub struct Value {
    pub foo: i32,
}

#[diapause::coroutine]
fn generics_example<T: Display>(x: T, y: &mut Value) {
    y.foo = 1;
    let _ = format!("{}", x);
}

#[test]
fn generics_and_reference_args() {
    let mut v = Value { foo: 0 };
    let mut c = generics_example("x", &mut v);
    assert_eq!(c.start(), CoroutineState::Complete(()));
    assert_eq!(v.foo, 1);
}

// "resume引数の受け渡し"
#[diapause::coroutine(yield = u32, resume = String)]
fn resume_example() -> usize {
    let r = yield_!(1);
    r.len()
}

#[test]
fn resume_argument_passing() {
    let mut c = resume_example();
    assert_eq!(c.start(), CoroutineState::Yielded(1));
    assert_eq!(c.resume("hello".to_string()), CoroutineState::Complete(5));
}

// "yieldをまたぐ借用"
#[diapause::coroutine(yield = i32)]
fn borrow_example() -> i32 {
    let mut x: Value = Value { foo: 1 };
    let y = &mut x;
    yield_!(123);
    y.foo = 99;
    x.foo
}

#[test]
fn borrow_across_yield() {
    let mut c = borrow_example();
    assert_eq!(c.start(), CoroutineState::Yielded(123));
    assert_eq!(c.resume(()), CoroutineState::Complete(99));
}

// "制御構文内のyield" — the v2 transformation example (also the design
// document's `totals`): a while loop whose body yields, compiled to a
// dispatch loop with a loop-header variant.
#[diapause::coroutine(yield = u32, resume = u32)]
fn totals(n: u32) -> u32 {
    let mut sum: u32 = 0;
    let mut i: u32 = 0;
    while i < n {
        let r = yield_!(sum);
        sum += r;
        i += 1;
    }
    sum
}

#[test]
fn control_flow_yield() {
    let mut c = totals(2);
    assert_eq!(c.start(), CoroutineState::Yielded(0));
    assert_eq!(c.resume(5), CoroutineState::Yielded(5));
    assert_eq!(c.resume(7), CoroutineState::Complete(12));
}

// Combined features: generics + multiple yields + borrow reconstruction
// + derive(Clone) snapshot.
#[diapause::coroutine(yield = usize, resume = u32)]
#[derive(Clone)]
fn combo<T: Clone + Into<u32>>(seed: T, mut out: Vec<u32>) -> Vec<u32> {
    let mut acc: u32 = seed.into();
    let p = &mut acc;
    let r = yield_!(out.len());
    *p += r;
    let r2 = yield_!(out.len() + 1);
    acc += r2;
    out.push(acc);
    out
}

#[test]
fn combined_features() {
    let mut c = combo(1u8, vec![0]);
    assert_eq!(c.start(), CoroutineState::Yielded(1));
    assert_eq!(c.resume(10), CoroutineState::Yielded(2));

    // Fork the suspended coroutine and drive both to completion.
    let mut snapshot = c.clone();
    assert_eq!(c.resume(100), CoroutineState::Complete(vec![0, 111]));
    assert_eq!(snapshot.resume(200), CoroutineState::Complete(vec![0, 211]));
}