type-flow-macros 0.2.0

Macros to make pipelines that encode the order of operation execution in the type signature of the pipeline and allow for functions that mutate that order.
Documentation
use type_flow_macros::*;
use type_flow_traits::*;
use type_flow_proc_macros::*;


processor!(AddFive, String, |s: String| s + "5");

#[test]
fn it_works() {
    let result = AddFive::process(String::from("test"));
    assert_eq!(result, "test5");
}

inplace_processor!(InplaceAddHash, String, TestError, |s: &mut String| {
    s.push_str("#");
    Ok(())
});

inplace_processor!(InplaceAddStar, String, TestError, |s: &mut String| {
    s.push_str("*");
    Ok(())
});

inplace_processor_pipeline!(InplacePipeline, String, TestError, InplaceAddHash, InplaceAddStar);

#[test]
fn test_inplace_pipeline() {
    let mut test_string = String::from("test");
    InplacePipeline::process(&mut test_string).unwrap();
    assert_eq!(test_string, "test#*");
}

inplace_processor!(ErrorProcessor, String, TestError, |_: &mut String| {
    Err(TestError::SomeError)
});

inplace_processor_pipeline!(ErrorPipeline, String, TestError, InplaceAddHash, ErrorProcessor, InplaceAddStar);

#[test]
fn test_error_pipeline() {
    let mut test_string = String::from("test");
    assert_eq!(ErrorPipeline::process(&mut test_string), Err(TestError::SomeError));
    assert_eq!(test_string, "test#");  // First processor executed before error
}

transform_processor!(SafeConvertToInt, String, i32, |s: String| s.parse::<i32>().unwrap_or(0));

#[test]
fn it_works_2() {
    let result = SafeConvertToInt::process(String::from("123"));
    assert_eq!(result, 123);
}

stateful_processor!(CountOfInvocations, u32, String, |state: &mut u32, s: String| {
    *state += 1;
    format!("{}: {}", s, state)
});

#[test]
fn it_works_3() {
    let mut processor = CountOfInvocations::new(5);
    let result = processor.process(String::from("Count"));
    assert_eq!(result, "Count: 6"); // The state is incremented, so the expected output should be 6.
}

stateful_processor!(FixedMultiply, u64, 10, String, |state:&mut u64, s: String| {
    *state *= 3;
    format!("{} fixed: {}", s, state)
});

#[test]
fn it_works_4() {
    let mut processor = FixedMultiply::instance();
    let result = processor.process(String::from("Counter"));
    assert_eq!(result, "Counter fixed: 30");
}

processor!(Reverse, String, |s: String| s.chars().rev().collect::<String>());
processor_pipeline!(StatelessPipeline, String, AddFive, Reverse);
#[test]
fn it_works_5() {
    let result = StatelessPipeline::process(String::from("test"));
    assert_eq!(result, "5tset");
}

processor_pipeline!(DoubleStatelessPipeline, String, StatelessPipeline, StatelessPipeline);

#[test]
fn it_works_6() {
    let result = DoubleStatelessPipeline::process(String::from("test"));
    assert_eq!(result, "5test5");
}

stateful_processor!(LetterAtoZPlacer, char, 'A', String, |state:&mut char, s: String| {
    if *state == 'Z' {
        *state = 'A';
    } else {
        *state = (*state as u8 + 1) as char;
    }
    format!("{}: {}", s, state)
});
#[test]
fn it_works_7() {
    let mut processor = LetterAtoZPlacer::instance();
    let result = processor.process(String::from("A"));
    assert_eq!(result, "A: B");
}

stateful_processor_pipeline!(WeirdStuff, String, a : LetterAtoZPlacer, b : FixedMultiply);

#[test]
fn it_works_8() {
    let processor_a = LetterAtoZPlacer::instance();
    let processor_b = FixedMultiply::instance();
    let mut pipeline = WeirdStuff::new(processor_a, processor_b);
    let result = pipeline.process(String::from("B"));
    assert_eq!(result, "B: B fixed: 30");
    let result = pipeline.process(String::from("C"));
    assert_eq!(result, "C: C fixed: 90");
    let result = pipeline.process(String::from("D"));
    assert_eq!(result, "D: D fixed: 270");
    let result = pipeline.process(String::from("E"));
    assert_eq!(result, "E: E fixed: 810");
    let result = pipeline.process(String::from("F"));
    assert_eq!(result, "F: F fixed: 2430");
    let result = pipeline.process(String::from("G"));
    assert_eq!(result, "G: G fixed: 7290");
    let result = pipeline.process(String::from("H"));
    assert_eq!(result, "H: H fixed: 21870");
    let result = pipeline.process(String::from("I"));
    assert_eq!(result, "I: I fixed: 65610");
    let result = pipeline.process(String::from("J"));
    assert_eq!(result, "J: J fixed: 196830");
    let _ = pipeline.process(String::from("K"));
    let _ = pipeline.process(String::from("L"));
    let _ = pipeline.process(String::from("M"));
    let _ = pipeline.process(String::from("N"));
    let _ = pipeline.process(String::from("O"));
    let _ = pipeline.process(String::from("P"));
    let _ = pipeline.process(String::from("Q"));
    let _ = pipeline.process(String::from("R"));
    let _ = pipeline.process(String::from("S"));
    let _ = pipeline.process(String::from("T"));
    let _ = pipeline.process(String::from("U"));
    let _ = pipeline.process(String::from("V"));
    let _ = pipeline.process(String::from("W"));
    let _ = pipeline.process(String::from("X"));
    let _ = pipeline.process(String::from("Y"));
    let result = pipeline.process(String::from("Z"));
    assert_eq!(result, "Z: Z fixed: 8472886094430");
    let result = pipeline.process(String::from("A"));
    assert_eq!(result, "A: A fixed: 25418658283290");
}

type_flow_processor_pipeline!(Test, String, P1, P2, P3);
#[test]
fn it_works_9() {
    //let that_thing: Test<AddFive, Reverse> = Test::<AddFive, Reverse> {  };
    let result = Test::<AddFive, Reverse, Test<AddFive, Reverse, AddFive>>::process(String::from("stuff"));
    assert_eq!(result, "5stuff55");
}

stateful_processor_pipeline!(SuffWeird, String, WeirdStuff, WeirdStuff);

#[test]
fn it_works_10() {
    let mut pipeline = SuffWeird::new(WeirdStuff::new(LetterAtoZPlacer::instance(), FixedMultiply::instance()), WeirdStuff::new(LetterAtoZPlacer::instance(), FixedMultiply::instance()));
    let result = pipeline.process(String::from("stuff"));
    assert_eq!(result, "stuff: B fixed: 30: B fixed: 30");
}

#[derive(Debug, PartialEq)]
pub enum TestError {
    SomeError,
}

inplace_processor!(InplaceAddFive, String, TestError, |s: &mut String| {
    s.push_str("5");
    Ok(())
});

inplace_processor!(InplaceReverse, String, TestError, |s: &mut String| {
    *s = s.chars().rev().collect::<String>();
    Ok(())
});

type_flow_inplace_processor_pipeline!(
    InplaceTestPipeline,
    String,
    TestError,
    P1,
    P2,
    P3
);

#[test]
fn it_works_inplace() {
    let mut data = String::from("stuff");
    let result = InplaceTestPipeline::<InplaceAddFive, InplaceReverse, InplaceAddFive>::process(&mut data);
    assert_eq!(result, Ok(()));
    assert_eq!(data, "5ffuts5");
}

inplace_stateful_processor!(CountUp, u32, String, TestError, |state: &mut u32, s: &mut String| {
    *state += 1;
    s.push_str(state.to_string().as_str());
    Ok(())
});
inplace_stateful_processor!(ToggleCase, bool, String, TestError, |state: &mut bool, s: &mut String| {
    *state = !*state;
    if *state {
        s.make_ascii_uppercase();
    } else {
        s.make_ascii_lowercase();
    }
    Ok(())
});
inplace_stateful_processor_pipeline!(InplaceStatefulProcessorPipeline, String, TestError, CountUp, ToggleCase);
#[test]
fn it_works_inplace_stateful_pipeline() {
    let mut pipeline = InplaceStatefulProcessorPipeline::new(CountUp::new(0), ToggleCase::new(false));
    let mut data = String::from("stuff");
    let result = pipeline.process(&mut data);
    assert_eq!(result, Ok(()));
    assert_eq!(data, "STUFF1");
    let result = pipeline.process(&mut data);
    assert_eq!(result, Ok(()));
    assert_eq!(data, "stuff12");
}

inplace_stateful_processor!(InplaceAddExclamation, (), String, TestError, |_, s: &mut String| {
    s.push_str("!");
    Ok(())
});

inplace_stateful_processor!(InplaceAddQuestion, (), String, TestError, |_, s: &mut String| {
    s.push_str("?");
    Ok(())
});

inplace_stateful_processor!(LimitedProcessor, u32, 0, String, TestError, |state: &mut u32, s: &mut String| {
    if *state >= 5 {
        Err(TestError::SomeError)
    } else {
        *state += 1;
        s.push_str(&format!(" #{}", state));
        Ok(())
    }
});

#[test]
fn test_limited_processor() {
    let mut processor = LimitedProcessor::instance();
    let mut test_string = String::from("Test");
    for i in 1..=5 {
        processor.process(&mut test_string).unwrap();
        assert_eq!(test_string, format!("Test #{}", (1..=i).map(|num| num.to_string()).collect::<Vec<String>>().join(" #")));
    }
    assert_eq!(processor.process(&mut test_string), Err(TestError::SomeError));
}


type_flow_inplace_stateful_processor_pipeline!(WhyNotNow, String, TestError, a : A, b : B, c : C);

#[test]
fn test_type_flow_inplace_stateful_processor_pipeline() {
    let mut go_with_the_flow = WhyNotNow::new(ToggleCase::new(false), InplaceAddExclamation::new(()), LimitedProcessor::instance());
    let mut test_string = String::from("Test");
    go_with_the_flow.process(&mut test_string).unwrap();
    assert_eq!(test_string, "TEST! #1");
    let mut swapped = go_with_the_flow.swap();
    swapped.process(&mut test_string).unwrap();
    assert_eq!(test_string, "test! #1 #2!");
    let mut shifted_left = swapped.shift_left();
    shifted_left.process(&mut test_string).unwrap();
    assert_eq!(test_string, "TEST! #1 #2!! #3");
    let mut shifted_right = shifted_left.shift_right();
    let _ = shifted_right.process(&mut test_string);
    let _ = shifted_right.process(&mut test_string);
    assert_eq!(shifted_right.process(&mut test_string), Err(TestError::SomeError));
    //let mut try_something = shifted_right::<0, 1>::swap_processors();
    let mut something = <WhyNotNow<LimitedProcessor, InplaceAddExclamation, ToggleCase> as SwapArbitraryProcessors<0, 1>>::swap_processors(shifted_right);
    assert_eq!(something.process(&mut test_string), Err(TestError::SomeError));
}

type_flow_inplace_stateful_processor_pipeline!(YupICan, String, TestError, 5);