partial_fn 0.1.0

Construct a partial function from a series of match statements.
Documentation
#![cfg_attr(feature = "unstable", feature(macro_at_most_once_rep))]

#[macro_use]
extern crate partial_fn;

use partial_fn::PartialFn;

#[derive(Debug)]
enum Test {
    Foo(i32),
    Bar(f32),
    Baz(bool),
}

macro_rules! test_partial_fn (
    ($var:expr, $lhs:expr, None) => (
        assert!(!$var.is_defined_at($lhs) && $var.call($lhs).is_none())
    );
    ($var:expr, $lhs:expr, $rhs:expr) => (
        assert!($var.is_defined_at($lhs) &&
            $var.call($lhs).is_some() &&
            $var.call($lhs) == Some($rhs)
        )
    );
);

#[test]
fn handles_basic_functionality()
{
    let pf = partial_fn!
    {
        "foo" => 1,
        "bar" => 2
    };

    test_partial_fn!(pf, "foo", 1);
    test_partial_fn!(pf, "bar", 2);
    test_partial_fn!(pf, "baz", None);
}

#[test]
fn handles_basic_functionality_with_default_case()
{
    let pf = partial_fn!
    {
        "foo" => 1,
        "bar" => 2,
        _ => 3
    };

    test_partial_fn!(pf, "foo", 1);
    test_partial_fn!(pf, "bar", 2);
    test_partial_fn!(pf, "baz", 3);
}

#[test]
fn handles_variable_capture()
{
    let pf = partial_fn!
    {
        Test::Foo(a) => a,
        Test::Bar(_b) => 1
    };

    test_partial_fn!(pf, Test::Foo(2), 2);
    test_partial_fn!(pf, Test::Bar(1.0), 1);
    test_partial_fn!(pf, Test::Baz(true), None);
}

#[test]
fn handles_non_local_variable_in_expression()
{
    let c = 1;

    let pf = partial_fn!
    {
        Test::Foo(a) => a + c
    };

    test_partial_fn!(pf, Test::Foo(1), 2);
    test_partial_fn!(pf, Test::Bar(1.0), None);
}

#[test]
fn handles_pattern_guards()
{
    let c1 = 1;
    let c2 = 2;

    let pf = partial_fn!
    {
        Test::Foo(a) if a == 1 => a + c1,
        Test::Foo(a) if a == 2 => a + c2
    };

    test_partial_fn!(pf, Test::Foo(1), 2);
    test_partial_fn!(pf, Test::Foo(2), 4);
    test_partial_fn!(pf, Test::Foo(3), None);
    test_partial_fn!(pf, Test::Bar(1.0), None);
}

#[test]
fn handles_pattern_alternation()
{
    let pf = partial_fn!
    {
        1 | 2 => "foo",
        3 => "bar"
    };

    test_partial_fn!(pf, 1, "foo");
    test_partial_fn!(pf, 2, "foo");
    test_partial_fn!(pf, 3, "bar");
    test_partial_fn!(pf, 4, None);
}

#[test]
fn handles_inclusive_range()
{
    let pf = partial_fn!
    {
        1..=3 => "foo"
    };

    test_partial_fn!(pf, 1, "foo");
    test_partial_fn!(pf, 2, "foo");
    test_partial_fn!(pf, 3, "foo");
    test_partial_fn!(pf, 4, None);
}

#[test]
fn handles_pattern_binding()
{
    let pf = partial_fn!
    {
        a @ "foo" => a
    };

    test_partial_fn!(pf, "foo", "foo");
    test_partial_fn!(pf, "bar", None);
}

#[test]
fn handles_all_of_it_mixed_together()
{
    let pf = partial_fn!
    {
        a @ 1..=10 | a @ 21..=30 if a % 2 == 0 => a/2,
        a @ 11..=20 | a @ 31..=40 if a % 2 == 1 => a
    };

    test_partial_fn!(pf, 1, None);
    test_partial_fn!(pf, 2, 1);
    test_partial_fn!(pf, 11, 11);
    test_partial_fn!(pf, 12, None);
    test_partial_fn!(pf, 21, None);
    test_partial_fn!(pf, 22, 11);
    test_partial_fn!(pf, 31, 31);
    test_partial_fn!(pf, 32, None);
}