qubit-function 0.16.0

Functional programming traits and Box/Rc/Arc adapters for Rust, inspired by Java functional interfaces
// =============================================================================
//    Copyright (c) 2025 - 2026 Haixing Hu.
//
//    SPDX-License-Identifier: Apache-2.0
//
//    Licensed under the Apache License, Version 2.0.
// =============================================================================

use std::{
    io,
    rc::Rc,
};

use qubit_function::{
    BoxCallable,
    BoxCallableWith,
    BoxRunnable,
    BoxRunnableWith,
    Callable,
    CallableWith,
    LocalBoxCallable,
    LocalBoxCallableWith,
    LocalBoxRunnable,
    LocalBoxRunnableWith,
    Runnable,
    RunnableWith,
};

#[cfg(feature = "once")]
use qubit_function::{
    BoxCallableOnce,
    BoxRunnableOnce,
    CallableOnce,
    LocalBoxCallableOnce,
    LocalBoxRunnableOnce,
    RunnableOnce,
};

/// Returns `value` after proving at compile time that it is transferable.
fn require_send<T: Send>(value: T) -> T {
    value
}

#[test]
fn test_task_box_compositions_remain_send() {
    let mut callable = require_send(
        BoxCallable::new(|| Ok::<i32, io::Error>(20))
            .map(|value| value + 1)
            .and_then(|value| Ok(value * 2)),
    );
    assert_eq!(callable.call().expect("callable should succeed"), 42);

    let mut runnable = require_send(
        BoxRunnable::new(|| Ok::<(), io::Error>(()))
            .and_then(|| Ok::<(), io::Error>(())),
    );
    runnable.run().expect("runnable should succeed");
}

#[test]
fn test_task_box_with_compositions_remain_send() {
    let mut callable = require_send(
        BoxCallableWith::new(|value: &mut i32| {
            *value += 1;
            Ok::<i32, io::Error>(*value)
        })
        .map(|value| value * 2)
        .and_then(|value, input| {
            *input += 1;
            Ok(value + *input)
        }),
    );
    let mut input = 20;
    assert_eq!(
        callable
            .call_with(&mut input)
            .expect("callable-with should succeed"),
        64
    );

    let mut runnable = require_send(
        BoxRunnableWith::new(|value: &mut i32| {
            *value += 1;
            Ok::<(), io::Error>(())
        })
        .and_then(|value: &mut i32| {
            *value *= 2;
            Ok::<(), io::Error>(())
        }),
    );
    let mut input = 20;
    runnable
        .run_with(&mut input)
        .expect("runnable-with should succeed");
    assert_eq!(input, 42);
}

#[cfg(feature = "once")]
#[test]
fn test_task_box_once_compositions_remain_send() {
    let callable = require_send(
        BoxCallableOnce::new(|| Ok::<i32, io::Error>(20))
            .map(|value| value + 1)
            .and_then(|value| Ok(value * 2)),
    );
    assert_eq!(
        callable.call_once().expect("callable-once should succeed"),
        42,
    );

    let runnable = require_send(
        BoxRunnableOnce::new(|| Ok::<(), io::Error>(()))
            .and_then(|| Ok::<(), io::Error>(())),
    );
    runnable.run_once().expect("runnable-once should succeed");
}

#[test]
fn test_local_task_box_compositions_accept_rc_captures() {
    let suffix = Rc::new(String::from("-local"));
    let callable_suffix = Rc::clone(&suffix);
    let mut callable = LocalBoxCallable::new(|| {
        Ok::<String, io::Error>(String::from("callable"))
    })
    .map(move |value| format!("{value}{callable_suffix}"));
    assert_eq!(
        callable.call().expect("local callable should succeed"),
        "callable-local"
    );

    let runnable_suffix = Rc::clone(&suffix);
    let mut runnable = LocalBoxRunnable::new(move || {
        assert_eq!(runnable_suffix.as_str(), "-local");
        Ok::<(), io::Error>(())
    })
    .and_then(|| Ok::<(), io::Error>(()));
    runnable.run().expect("local runnable should succeed");
}

#[test]
fn test_local_task_box_with_compositions_accept_rc_captures() {
    let offset = Rc::new(1);
    let callable_offset = Rc::clone(&offset);
    let mut callable = LocalBoxCallableWith::new(move |value: &mut i32| {
        *value += *callable_offset;
        Ok::<i32, io::Error>(*value)
    })
    .map(|value| value * 2);
    let mut input = 20;
    assert_eq!(
        callable
            .call_with(&mut input)
            .expect("local callable-with should succeed"),
        42
    );

    let runnable_offset = Rc::clone(&offset);
    let mut runnable = LocalBoxRunnableWith::new(move |value: &mut i32| {
        *value += *runnable_offset;
        Ok::<(), io::Error>(())
    })
    .and_then(|value: &mut i32| {
        *value *= 2;
        Ok::<(), io::Error>(())
    });
    let mut input = 20;
    runnable
        .run_with(&mut input)
        .expect("local runnable-with should succeed");
    assert_eq!(input, 42);
}

#[cfg(feature = "once")]
#[test]
fn test_local_task_box_once_compositions_accept_rc_captures() {
    let suffix = Rc::new(String::from("-local"));
    let callable_suffix = Rc::clone(&suffix);
    let callable = LocalBoxCallableOnce::new(|| {
        Ok::<String, io::Error>(String::from("callable"))
    })
    .map(move |value| format!("{value}{callable_suffix}"));
    assert_eq!(
        callable
            .call_once()
            .expect("local callable-once should succeed"),
        "callable-local"
    );

    let runnable_suffix = Rc::clone(&suffix);
    let runnable = LocalBoxRunnableOnce::new(move || {
        assert_eq!(runnable_suffix.as_str(), "-local");
        Ok::<(), io::Error>(())
    })
    .and_then(|| Ok::<(), io::Error>(()));
    runnable
        .run_once()
        .expect("local runnable-once should succeed");
}