Test-Implementation Pair

Implementation: crates/euclid/src/backend/vir_interpreter.rs
================================================================================
pub mod types;

use std::fmt::Debug;

use serde::{Deserialize, Serialize};

use crate::{
    backend::{self, inputs, EuclidBackend},
    frontend::{
        ast,
        dir::{self, EuclidDirFilter},
        vir,
    },
};

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct VirInterpreterBackend<O> {
    program: vir::ValuedProgram<O>,
}

impl<O> VirInterpreterBackend<O>
where
    O: Clone,
{
    #[inline]
    fn eval_comparison(comp: &vir::ValuedComparison, ctx: &types::Context) -> bool {
        match &comp.logic {
            vir::ValuedComparisonLogic::PositiveDisjunction => {
                comp.values.iter().any(|v| ctx.check_presence(v))
            }
            vir::ValuedComparisonLogic::NegativeConjunction => {
                comp.values.iter().all(|v| !ctx.check_presence(v))
            }
        }
    }

    #[inline]
    fn eval_condition(cond: &vir::ValuedIfCondition, ctx: &types::Context) -> bool {
        cond.iter().all(|comp| Self::eval_comparison(comp, ctx))
    }

    fn eval_statement(stmt: &vir::ValuedIfStatement, ctx: &types::Context) -> bool {
        if Self::eval_condition(&stmt.condition, ctx) {
            {
                stmt.nested.as_ref().is_none_or(|nested_stmts| {
                    nested_stmts.iter().any(|s| Self::eval_statement(s, ctx))
                })
            }
        } else {
            false
        }
    }

    fn eval_rule(rule: &vir::ValuedRule<O>, ctx: &types::Context) -> bool {
        rule.statements
            .iter()
            .any(|stmt| Self::eval_statement(stmt, ctx))
    }

    fn eval_program(
        program: &vir::ValuedProgram<O>,
        ctx: &types::Context,
    ) -> backend::BackendOutput<O> {
        program
            .rules
            .iter()
            .find(|rule| Self::eval_rule(rule, ctx))
            .map_or_else(
                || backend::BackendOutput {
                    connector_selection: program.default_selection.clone(),
                    rule_name: None,
                },
                |rule| backend::BackendOutput {
                    connector_selection: rule.connector_selection.clone(),
                    rule_name: Some(rule.name.clone()),
                },
            )
    }
}

impl<O> EuclidBackend<O> for VirInterpreterBackend<O>
where
    O: Clone + EuclidDirFilter,
{
    type Error = types::VirInterpreterError;

    fn with_program(program: ast::Program<O>) -> Result<Self, Self::Error> {
        let dir_program = ast::lowering::lower_program(program)
            .map_err(types::VirInterpreterError::LoweringError)?;

        let vir_program = dir::lowering::lower_program(dir_program)
            .map_err(types::VirInterpreterError::LoweringError)?;

        Ok(Self {
            program: vir_program,
        })
    }

    fn execute(
        &self,
        input: inputs::BackendInput,
    ) -> Result<backend::BackendOutput<O>, Self::Error> {
        let ctx = types::Context::from_input(input);
        Ok(Self::eval_program(&self.program, &ctx))
    }
}
#[cfg(all(test, feature = "ast_parser"))]
mod test {
    use common_utils::types::MinorUnit;
    use rustc_hash::FxHashMap;

    use super::*;
    use crate::{enums, types::DummyOutput};

    #[test]
    fn test_execution() {
        let program_str = r#"
        default: [ "stripe",  "adyen"]

        rule_1: ["stripe"]
        {
            pay_later = klarna
        }

        rule_2: ["adyen"]
        {
            pay_later = affirm
        }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                card_bin: None,
                extended_card_bin: None,
                currency: enums::Currency::USD,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_2");
    }
    #[test]
    fn test_payment_type() {
        let program_str = r#"
        default: ["stripe", "adyen"]
        rule_1: ["stripe"]
        {
           payment_type = setup_mandate
        }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                currency: enums::Currency::USD,
                card_bin: Some("123456".to_string()),
                extended_card_bin: None,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: Some(enums::PaymentType::SetupMandate),
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_1");
    }

    #[test]
    fn test_ppt_flow() {
        let program_str = r#"
        default: ["stripe", "adyen"]
        rule_1: ["stripe"]
        {
           payment_type = ppt_mandate
        }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                currency: enums::Currency::USD,
                card_bin: Some("123456".to_string()),
                extended_card_bin: None,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: Some(enums::PaymentType::PptMandate),
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_1");
    }

    #[test]
    fn test_mandate_type() {
        let program_str = r#"
        default: ["stripe", "adyen"]
        rule_1: ["stripe"]
        {
           mandate_type = single_use
        }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                currency: enums::Currency::USD,
                card_bin: Some("123456".to_string()),
                extended_card_bin: None,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: Some(enums::MandateType::SingleUse),
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_1");
    }

    #[test]
    fn test_mandate_acceptance_type() {
        let program_str = r#"
        default: ["stripe","adyen"]
        rule_1: ["stripe"]
        {
           mandate_acceptance_type = online
        }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                currency: enums::Currency::USD,
                card_bin: Some("123456".to_string()),
                extended_card_bin: None,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: Some(enums::MandateAcceptanceType::Online),
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_1");
    }
    #[test]
    fn test_card_bin() {
        let program_str = r#"
        default: ["stripe", "adyen"]

        rule_1: ["stripe"]
        {
           card_bin="123456"
        }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                currency: enums::Currency::USD,
                card_bin: Some("123456".to_string()),
                extended_card_bin: None,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_1");
    }
    #[test]
    fn test_payment_amount() {
        let program_str = r#"
        default: ["stripe", "adyen"]

        rule_1: ["stripe"]
        {
           amount = 32
        }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                currency: enums::Currency::USD,
                card_bin: None,
                extended_card_bin: None,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_1");
    }
    #[test]
    fn test_payment_method() {
        let program_str = r#"
        default: ["stripe", "adyen"]

        rule_1: ["stripe"]
        {
           payment_method = pay_later
        }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                currency: enums::Currency::USD,
                card_bin: None,
                extended_card_bin: None,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_1");
    }
    #[test]
    fn test_future_usage() {
        let program_str = r#"
        default: ["stripe", "adyen"]

        rule_1: ["stripe"]
        {
           setup_future_usage = off_session
        }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                currency: enums::Currency::USD,
                card_bin: None,
                extended_card_bin: None,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: Some(enums::SetupFutureUsage::OffSession),
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_1");
    }

    #[test]
    fn test_metadata_execution() {
        let program_str = r#"
        default: ["stripe"," adyen"]

        rule_1: ["stripe"]
        {
        "metadata_key" = "arbitrary meta"
        }
        "#;
        let mut meta_map = FxHashMap::default();
        meta_map.insert("metadata_key".to_string(), "arbitrary meta".to_string());
        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: Some(meta_map),
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                card_bin: None,
                extended_card_bin: None,
                currency: enums::Currency::USD,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_1");
    }

    #[test]
    fn test_less_than_operator() {
        let program_str = r#"
        default: ["stripe", "adyen"]

        rule_1: ["stripe"]
        {
           amount>=123
        }
        "#;
        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp_greater = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(150),
                card_bin: None,
                extended_card_bin: None,
                currency: enums::Currency::USD,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };
        let mut inp_equal = inp_greater.clone();
        inp_equal.payment.amount = MinorUnit::new(123);
        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result_greater = backend.execute(inp_greater).expect("Execution");
        let result_equal = backend.execute(inp_equal).expect("Execution");
        assert_eq!(
            result_equal.rule_name.expect("Rule Name").as_str(),
            "rule_1"
        );
        assert_eq!(
            result_greater.rule_name.expect("Rule Name").as_str(),
            "rule_1"
        );
    }

    #[test]
    fn test_greater_than_operator() {
        let program_str = r#"
        default: ["stripe", "adyen"]

        rule_1: ["stripe"]
        {
           amount<=123
        }
        "#;
        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp_lower = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(120),
                card_bin: None,
                extended_card_bin: None,
                currency: enums::Currency::USD,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };
        let mut inp_equal = inp_lower.clone();
        inp_equal.payment.amount = MinorUnit::new(123);
        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result_equal = backend.execute(inp_equal).expect("Execution");
        let result_lower = backend.execute(inp_lower).expect("Execution");
        assert_eq!(
            result_equal.rule_name.expect("Rule Name").as_str(),
            "rule_1"
        );
        assert_eq!(
            result_lower.rule_name.expect("Rule Name").as_str(),
            "rule_1"
        );
    }
}

Tests: crates/euclid/src/backend/vir_interpreter.rs
================================================================================
pub mod types;

use std::fmt::Debug;

use serde::{Deserialize, Serialize};

use crate::{
    backend::{self, inputs, EuclidBackend},
    frontend::{
        ast,
        dir::{self, EuclidDirFilter},
        vir,
    },
};

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct VirInterpreterBackend<O> {
    program: vir::ValuedProgram<O>,
}

impl<O> VirInterpreterBackend<O>
where
    O: Clone,
{
    #[inline]
    fn eval_comparison(comp: &vir::ValuedComparison, ctx: &types::Context) -> bool {
        match &comp.logic {
            vir::ValuedComparisonLogic::PositiveDisjunction => {
                comp.values.iter().any(|v| ctx.check_presence(v))
            }
            vir::ValuedComparisonLogic::NegativeConjunction => {
                comp.values.iter().all(|v| !ctx.check_presence(v))
            }
        }
    }

    #[inline]
    fn eval_condition(cond: &vir::ValuedIfCondition, ctx: &types::Context) -> bool {
        cond.iter().all(|comp| Self::eval_comparison(comp, ctx))
    }

    fn eval_statement(stmt: &vir::ValuedIfStatement, ctx: &types::Context) -> bool {
        if Self::eval_condition(&stmt.condition, ctx) {
            {
                stmt.nested.as_ref().is_none_or(|nested_stmts| {
                    nested_stmts.iter().any(|s| Self::eval_statement(s, ctx))
                })
            }
        } else {
            false
        }
    }

    fn eval_rule(rule: &vir::ValuedRule<O>, ctx: &types::Context) -> bool {
        rule.statements
            .iter()
            .any(|stmt| Self::eval_statement(stmt, ctx))
    }

    fn eval_program(
        program: &vir::ValuedProgram<O>,
        ctx: &types::Context,
    ) -> backend::BackendOutput<O> {
        program
            .rules
            .iter()
            .find(|rule| Self::eval_rule(rule, ctx))
            .map_or_else(
                || backend::BackendOutput {
                    connector_selection: program.default_selection.clone(),
                    rule_name: None,
                },
                |rule| backend::BackendOutput {
                    connector_selection: rule.connector_selection.clone(),
                    rule_name: Some(rule.name.clone()),
                },
            )
    }
}

impl<O> EuclidBackend<O> for VirInterpreterBackend<O>
where
    O: Clone + EuclidDirFilter,
{
    type Error = types::VirInterpreterError;

    fn with_program(program: ast::Program<O>) -> Result<Self, Self::Error> {
        let dir_program = ast::lowering::lower_program(program)
            .map_err(types::VirInterpreterError::LoweringError)?;

        let vir_program = dir::lowering::lower_program(dir_program)
            .map_err(types::VirInterpreterError::LoweringError)?;

        Ok(Self {
            program: vir_program,
        })
    }

    fn execute(
        &self,
        input: inputs::BackendInput,
    ) -> Result<backend::BackendOutput<O>, Self::Error> {
        let ctx = types::Context::from_input(input);
        Ok(Self::eval_program(&self.program, &ctx))
    }
}
#[cfg(all(test, feature = "ast_parser"))]
mod test {
    use common_utils::types::MinorUnit;
    use rustc_hash::FxHashMap;

    use super::*;
    use crate::{enums, types::DummyOutput};

    #[test]
    fn test_execution() {
        let program_str = r#"
        default: [ "stripe",  "adyen"]

        rule_1: ["stripe"]
        {
            pay_later = klarna
        }

        rule_2: ["adyen"]
        {
            pay_later = affirm
        }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                card_bin: None,
                extended_card_bin: None,
                currency: enums::Currency::USD,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_2");
    }
    #[test]
    fn test_payment_type() {
        let program_str = r#"
        default: ["stripe", "adyen"]
        rule_1: ["stripe"]
        {
           payment_type = setup_mandate
        }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                currency: enums::Currency::USD,
                card_bin: Some("123456".to_string()),
                extended_card_bin: None,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: Some(enums::PaymentType::SetupMandate),
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_1");
    }

    #[test]
    fn test_ppt_flow() {
        let program_str = r#"
        default: ["stripe", "adyen"]
        rule_1: ["stripe"]
        {
           payment_type = ppt_mandate
        }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                currency: enums::Currency::USD,
                card_bin: Some("123456".to_string()),
                extended_card_bin: None,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: Some(enums::PaymentType::PptMandate),
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_1");
    }

    #[test]
    fn test_mandate_type() {
        let program_str = r#"
        default: ["stripe", "adyen"]
        rule_1: ["stripe"]
        {
           mandate_type = single_use
        }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                currency: enums::Currency::USD,
                card_bin: Some("123456".to_string()),
                extended_card_bin: None,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: Some(enums::MandateType::SingleUse),
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_1");
    }

    #[test]
    fn test_mandate_acceptance_type() {
        let program_str = r#"
        default: ["stripe","adyen"]
        rule_1: ["stripe"]
        {
           mandate_acceptance_type = online
        }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                currency: enums::Currency::USD,
                card_bin: Some("123456".to_string()),
                extended_card_bin: None,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: Some(enums::MandateAcceptanceType::Online),
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_1");
    }
    #[test]
    fn test_card_bin() {
        let program_str = r#"
        default: ["stripe", "adyen"]

        rule_1: ["stripe"]
        {
           card_bin="123456"
        }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                currency: enums::Currency::USD,
                card_bin: Some("123456".to_string()),
                extended_card_bin: None,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_1");
    }
    #[test]
    fn test_payment_amount() {
        let program_str = r#"
        default: ["stripe", "adyen"]

        rule_1: ["stripe"]
        {
           amount = 32
        }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                currency: enums::Currency::USD,
                card_bin: None,
                extended_card_bin: None,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_1");
    }
    #[test]
    fn test_payment_method() {
        let program_str = r#"
        default: ["stripe", "adyen"]

        rule_1: ["stripe"]
        {
           payment_method = pay_later
        }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                currency: enums::Currency::USD,
                card_bin: None,
                extended_card_bin: None,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_1");
    }
    #[test]
    fn test_future_usage() {
        let program_str = r#"
        default: ["stripe", "adyen"]

        rule_1: ["stripe"]
        {
           setup_future_usage = off_session
        }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                currency: enums::Currency::USD,
                card_bin: None,
                extended_card_bin: None,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: Some(enums::SetupFutureUsage::OffSession),
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_1");
    }

    #[test]
    fn test_metadata_execution() {
        let program_str = r#"
        default: ["stripe"," adyen"]

        rule_1: ["stripe"]
        {
        "metadata_key" = "arbitrary meta"
        }
        "#;
        let mut meta_map = FxHashMap::default();
        meta_map.insert("metadata_key".to_string(), "arbitrary meta".to_string());
        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: Some(meta_map),
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                card_bin: None,
                extended_card_bin: None,
                currency: enums::Currency::USD,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_1");
    }

    #[test]
    fn test_less_than_operator() {
        let program_str = r#"
        default: ["stripe", "adyen"]

        rule_1: ["stripe"]
        {
           amount>=123
        }
        "#;
        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp_greater = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(150),
                card_bin: None,
                extended_card_bin: None,
                currency: enums::Currency::USD,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };
        let mut inp_equal = inp_greater.clone();
        inp_equal.payment.amount = MinorUnit::new(123);
        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result_greater = backend.execute(inp_greater).expect("Execution");
        let result_equal = backend.execute(inp_equal).expect("Execution");
        assert_eq!(
            result_equal.rule_name.expect("Rule Name").as_str(),
            "rule_1"
        );
        assert_eq!(
            result_greater.rule_name.expect("Rule Name").as_str(),
            "rule_1"
        );
    }

    #[test]
    fn test_greater_than_operator() {
        let program_str = r#"
        default: ["stripe", "adyen"]

        rule_1: ["stripe"]
        {
           amount<=123
        }
        "#;
        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp_lower = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(120),
                card_bin: None,
                extended_card_bin: None,
                currency: enums::Currency::USD,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };
        let mut inp_equal = inp_lower.clone();
        inp_equal.payment.amount = MinorUnit::new(123);
        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result_equal = backend.execute(inp_equal).expect("Execution");
        let result_lower = backend.execute(inp_lower).expect("Execution");
        assert_eq!(
            result_equal.rule_name.expect("Rule Name").as_str(),
            "rule_1"
        );
        assert_eq!(
            result_lower.rule_name.expect("Rule Name").as_str(),
            "rule_1"
        );
    }
}


Test-Implementation Pair

Implementation: crates/euclid/src/dssa/state_machine.rs
================================================================================
use super::types::EuclidAnalysable;
use crate::{dssa::types, frontend::dir, types::Metadata};

#[derive(Debug, Clone, serde::Serialize, thiserror::Error)]
#[serde(tag = "type", content = "info", rename_all = "snake_case")]
pub enum StateMachineError {
    #[error("Index out of bounds: {0}")]
    IndexOutOfBounds(&'static str),
}

#[derive(Debug)]
struct ComparisonStateMachine<'a> {
    values: &'a [dir::DirValue],
    logic: &'a dir::DirComparisonLogic,
    metadata: &'a Metadata,
    count: usize,
    ctx_idx: usize,
}

impl<'a> ComparisonStateMachine<'a> {
    #[inline]
    fn is_finished(&self) -> bool {
        self.count + 1 >= self.values.len()
            || matches!(self.logic, dir::DirComparisonLogic::NegativeConjunction)
    }

    #[inline]
    fn advance(&mut self) {
        if let dir::DirComparisonLogic::PositiveDisjunction = self.logic {
            self.count = (self.count + 1) % self.values.len();
        }
    }

    #[inline]
    fn reset(&mut self) {
        self.count = 0;
    }

    #[inline]
    fn put(&self, context: &mut types::ConjunctiveContext<'a>) -> Result<(), StateMachineError> {
        if let dir::DirComparisonLogic::PositiveDisjunction = self.logic {
            *context
                .get_mut(self.ctx_idx)
                .ok_or(StateMachineError::IndexOutOfBounds(
                    "in ComparisonStateMachine while indexing into context",
                ))? = types::ContextValue::assertion(
                self.values
                    .get(self.count)
                    .ok_or(StateMachineError::IndexOutOfBounds(
                        "in ComparisonStateMachine while indexing into values",
                    ))?,
                self.metadata,
            );
        }
        Ok(())
    }

    #[inline]
    fn push(&self, context: &mut types::ConjunctiveContext<'a>) -> Result<(), StateMachineError> {
        match self.logic {
            dir::DirComparisonLogic::PositiveDisjunction => {
                context.push(types::ContextValue::assertion(
                    self.values
                        .get(self.count)
                        .ok_or(StateMachineError::IndexOutOfBounds(
                            "in ComparisonStateMachine while pushing",
                        ))?,
                    self.metadata,
                ));
            }

            dir::DirComparisonLogic::NegativeConjunction => {
                context.push(types::ContextValue::negation(self.values, self.metadata));
            }
        }
        Ok(())
    }
}

#[derive(Debug)]
struct ConditionStateMachine<'a> {
    state_machines: Vec<ComparisonStateMachine<'a>>,
    start_ctx_idx: usize,
}

impl<'a> ConditionStateMachine<'a> {
    fn new(condition: &'a [dir::DirComparison], start_idx: usize) -> Self {
        let mut machines = Vec::<ComparisonStateMachine<'a>>::with_capacity(condition.len());

        let mut machine_idx = start_idx;
        for cond in condition {
            let machine = ComparisonStateMachine {
                values: &cond.values,
                logic: &cond.logic,
                metadata: &cond.metadata,
                count: 0,
                ctx_idx: machine_idx,
            };
            machines.push(machine);
            machine_idx += 1;
        }

        Self {
            state_machines: machines,
            start_ctx_idx: start_idx,
        }
    }

    fn init(&self, context: &mut types::ConjunctiveContext<'a>) -> Result<(), StateMachineError> {
        for machine in &self.state_machines {
            machine.push(context)?;
        }
        Ok(())
    }

    #[inline]
    fn destroy(&self, context: &mut types::ConjunctiveContext<'a>) {
        context.truncate(self.start_ctx_idx);
    }

    #[inline]
    fn is_finished(&self) -> bool {
        !self
            .state_machines
            .iter()
            .any(|machine| !machine.is_finished())
    }

    #[inline]
    fn get_next_ctx_idx(&self) -> usize {
        self.start_ctx_idx + self.state_machines.len()
    }

    fn advance(
        &mut self,
        context: &mut types::ConjunctiveContext<'a>,
    ) -> Result<(), StateMachineError> {
        for machine in self.state_machines.iter_mut().rev() {
            if machine.is_finished() {
                machine.reset();
                machine.put(context)?;
            } else {
                machine.advance();
                machine.put(context)?;
                break;
            }
        }
        Ok(())
    }
}

#[derive(Debug)]
struct IfStmtStateMachine<'a> {
    condition_machine: ConditionStateMachine<'a>,
    nested: Vec<&'a dir::DirIfStatement>,
    nested_idx: usize,
}

impl<'a> IfStmtStateMachine<'a> {
    fn new(stmt: &'a dir::DirIfStatement, ctx_start_idx: usize) -> Self {
        let condition_machine = ConditionStateMachine::new(&stmt.condition, ctx_start_idx);
        let nested: Vec<&'a dir::DirIfStatement> = match &stmt.nested {
            None => Vec::new(),
            Some(nested_stmts) => nested_stmts.iter().collect(),
        };

        Self {
            condition_machine,
            nested,
            nested_idx: 0,
        }
    }

    fn init(
        &self,
        context: &mut types::ConjunctiveContext<'a>,
    ) -> Result<Option<Self>, StateMachineError> {
        self.condition_machine.init(context)?;
        Ok(self
            .nested
            .first()
            .map(|nested| Self::new(nested, self.condition_machine.get_next_ctx_idx())))
    }

    #[inline]
    fn is_finished(&self) -> bool {
        self.nested_idx + 1 >= self.nested.len()
    }

    #[inline]
    fn is_condition_machine_finished(&self) -> bool {
        self.condition_machine.is_finished()
    }

    #[inline]
    fn destroy(&self, context: &mut types::ConjunctiveContext<'a>) {
        self.condition_machine.destroy(context);
    }

    #[inline]
    fn advance_condition_machine(
        &mut self,
        context: &mut types::ConjunctiveContext<'a>,
    ) -> Result<(), StateMachineError> {
        self.condition_machine.advance(context)?;
        Ok(())
    }

    fn advance(&mut self) -> Result<Option<Self>, StateMachineError> {
        if self.nested.is_empty() {
            Ok(None)
        } else {
            self.nested_idx = (self.nested_idx + 1) % self.nested.len();
            Ok(Some(Self::new(
                self.nested
                    .get(self.nested_idx)
                    .ok_or(StateMachineError::IndexOutOfBounds(
                        "in IfStmtStateMachine while advancing",
                    ))?,
                self.condition_machine.get_next_ctx_idx(),
            )))
        }
    }
}

#[derive(Debug)]
struct RuleStateMachine<'a> {
    connector_selection_data: &'a [(dir::DirValue, Metadata)],
    connectors_added: bool,
    if_stmt_machines: Vec<IfStmtStateMachine<'a>>,
    running_stack: Vec<IfStmtStateMachine<'a>>,
}

impl<'a> RuleStateMachine<'a> {
    fn new<O>(
        rule: &'a dir::DirRule<O>,
        connector_selection_data: &'a [(dir::DirValue, Metadata)],
    ) -> Self {
        let mut if_stmt_machines: Vec<IfStmtStateMachine<'a>> =
            Vec::with_capacity(rule.statements.len());

        for stmt in rule.statements.iter().rev() {
            if_stmt_machines.push(IfStmtStateMachine::new(
                stmt,
                connector_selection_data.len(),
            ));
        }

        Self {
            connector_selection_data,
            connectors_added: false,
            if_stmt_machines,
            running_stack: Vec::new(),
        }
    }

    fn is_finished(&self) -> bool {
        self.if_stmt_machines.is_empty() && self.running_stack.is_empty()
    }

    fn init_next(
        &mut self,
        context: &mut types::ConjunctiveContext<'a>,
    ) -> Result<(), StateMachineError> {
        if self.if_stmt_machines.is_empty() || !self.running_stack.is_empty() {
            return Ok(());
        }

        if !self.connectors_added {
            for (dir_val, metadata) in self.connector_selection_data {
                context.push(types::ContextValue::assertion(dir_val, metadata));
            }
            self.connectors_added = true;
        }

        context.truncate(self.connector_selection_data.len());

        if let Some(mut next_running) = self.if_stmt_machines.pop() {
            while let Some(nested_running) = next_running.init(context)? {
                self.running_stack.push(next_running);
                next_running = nested_running;
            }

            self.running_stack.push(next_running);
        }

        Ok(())
    }

    fn advance(
        &mut self,
        context: &mut types::ConjunctiveContext<'a>,
    ) -> Result<(), StateMachineError> {
        let mut condition_machines_finished = true;

        for stmt_machine in self.running_stack.iter_mut().rev() {
            if !stmt_machine.is_condition_machine_finished() {
                condition_machines_finished = false;
                stmt_machine.advance_condition_machine(context)?;
                break;
            } else {
                stmt_machine.advance_condition_machine(context)?;
            }
        }

        if !condition_machines_finished {
            return Ok(());
        }

        let mut maybe_next_running: Option<IfStmtStateMachine<'a>> = None;

        while let Some(last) = self.running_stack.last_mut() {
            if !last.is_finished() {
                maybe_next_running = last.advance()?;
                break;
            } else {
                last.destroy(context);
                self.running_stack.pop();
            }
        }

        if let Some(mut next_running) = maybe_next_running {
            while let Some(nested_running) = next_running.init(context)? {
                self.running_stack.push(next_running);
                next_running = nested_running;
            }

            self.running_stack.push(next_running);
        } else {
            self.init_next(context)?;
        }

        Ok(())
    }
}

#[derive(Debug)]
pub struct RuleContextManager<'a> {
    context: types::ConjunctiveContext<'a>,
    machine: RuleStateMachine<'a>,
    init: bool,
}

impl<'a> RuleContextManager<'a> {
    pub fn new<O>(
        rule: &'a dir::DirRule<O>,
        connector_selection_data: &'a [(dir::DirValue, Metadata)],
    ) -> Self {
        Self {
            context: Vec::new(),
            machine: RuleStateMachine::new(rule, connector_selection_data),
            init: false,
        }
    }

    pub fn advance(&mut self) -> Result<Option<&types::ConjunctiveContext<'a>>, StateMachineError> {
        if !self.init {
            self.init = true;
            self.machine.init_next(&mut self.context)?;
            Ok(Some(&self.context))
        } else if self.machine.is_finished() {
            Ok(None)
        } else {
            self.machine.advance(&mut self.context)?;

            if self.machine.is_finished() {
                Ok(None)
            } else {
                Ok(Some(&self.context))
            }
        }
    }

    pub fn advance_mut(
        &mut self,
    ) -> Result<Option<&mut types::ConjunctiveContext<'a>>, StateMachineError> {
        if !self.init {
            self.init = true;
            self.machine.init_next(&mut self.context)?;
            Ok(Some(&mut self.context))
        } else if self.machine.is_finished() {
            Ok(None)
        } else {
            self.machine.advance(&mut self.context)?;

            if self.machine.is_finished() {
                Ok(None)
            } else {
                Ok(Some(&mut self.context))
            }
        }
    }
}

#[derive(Debug)]
pub struct ProgramStateMachine<'a> {
    rule_machines: Vec<RuleStateMachine<'a>>,
    current_rule_machine: Option<RuleStateMachine<'a>>,
    is_init: bool,
}

impl<'a> ProgramStateMachine<'a> {
    pub fn new<O>(
        program: &'a dir::DirProgram<O>,
        connector_selection_data: &'a [Vec<(dir::DirValue, Metadata)>],
    ) -> Self {
        let mut rule_machines: Vec<RuleStateMachine<'a>> = program
            .rules
            .iter()
            .zip(connector_selection_data.iter())
            .rev()
            .map(|(rule, connector_selection_data)| {
                RuleStateMachine::new(rule, connector_selection_data)
            })
            .collect();

        Self {
            current_rule_machine: rule_machines.pop(),
            rule_machines,
            is_init: false,
        }
    }

    pub fn is_finished(&self) -> bool {
        self.current_rule_machine
            .as_ref()
            .is_none_or(|rsm| rsm.is_finished())
            && self.rule_machines.is_empty()
    }

    pub fn init(
        &mut self,
        context: &mut types::ConjunctiveContext<'a>,
    ) -> Result<(), StateMachineError> {
        if !self.is_init {
            if let Some(rsm) = self.current_rule_machine.as_mut() {
                rsm.init_next(context)?;
            }
            self.is_init = true;
        }

        Ok(())
    }

    pub fn advance(
        &mut self,
        context: &mut types::ConjunctiveContext<'a>,
    ) -> Result<(), StateMachineError> {
        if self
            .current_rule_machine
            .as_ref()
            .is_none_or(|rsm| rsm.is_finished())
        {
            self.current_rule_machine = self.rule_machines.pop();
            context.clear();
            if let Some(rsm) = self.current_rule_machine.as_mut() {
                rsm.init_next(context)?;
            }
        } else if let Some(rsm) = self.current_rule_machine.as_mut() {
            rsm.advance(context)?;
        }

        Ok(())
    }
}

pub struct AnalysisContextManager<'a> {
    context: types::ConjunctiveContext<'a>,
    machine: ProgramStateMachine<'a>,
    init: bool,
}

impl<'a> AnalysisContextManager<'a> {
    pub fn new<O>(
        program: &'a dir::DirProgram<O>,
        connector_selection_data: &'a [Vec<(dir::DirValue, Metadata)>],
    ) -> Self {
        let machine = ProgramStateMachine::new(program, connector_selection_data);
        let context: types::ConjunctiveContext<'a> = Vec::new();

        Self {
            context,
            machine,
            init: false,
        }
    }

    pub fn advance(&mut self) -> Result<Option<&types::ConjunctiveContext<'a>>, StateMachineError> {
        if !self.init {
            self.init = true;
            self.machine.init(&mut self.context)?;
            Ok(Some(&self.context))
        } else if self.machine.is_finished() {
            Ok(None)
        } else {
            self.machine.advance(&mut self.context)?;

            if self.machine.is_finished() {
                Ok(None)
            } else {
                Ok(Some(&self.context))
            }
        }
    }
}

pub fn make_connector_selection_data<O: EuclidAnalysable>(
    program: &dir::DirProgram<O>,
) -> Vec<Vec<(dir::DirValue, Metadata)>> {
    program
        .rules
        .iter()
        .map(|rule| {
            rule.connector_selection
                .get_dir_value_for_analysis(rule.name.clone())
        })
        .collect()
}

#[cfg(all(test, feature = "ast_parser"))]
mod tests {
    use super::*;
    use crate::{dirval, frontend::ast, types::DummyOutput};

    #[test]
    fn test_correct_contexts() {
        let program_str = r#"
            default: ["stripe", "adyen"]

            stripe_first: ["stripe", "adyen"]
            {
                payment_method = wallet {
                    payment_method = (card, bank_redirect) {
                        currency = USD
                        currency = GBP
                    }

                    payment_method = pay_later {
                        capture_method = automatic
                        capture_method = manual
                    }
                }

                payment_method = card {
                    payment_method = (card, bank_redirect) & capture_method = (automatic, manual) {
                        currency = (USD, GBP)
                    }
                }
            }
        "#;
        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let lowered = ast::lowering::lower_program(program).expect("Lowering");

        let selection_data = make_connector_selection_data(&lowered);
        let mut state_machine = ProgramStateMachine::new(&lowered, &selection_data);
        let mut ctx: types::ConjunctiveContext<'_> = Vec::new();
        state_machine.init(&mut ctx).expect("State machine init");

        let expected_contexts: Vec<Vec<dir::DirValue>> = vec![
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Wallet),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentCurrency = USD),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Wallet),
                dirval!(PaymentMethod = BankRedirect),
                dirval!(PaymentCurrency = USD),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Wallet),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentCurrency = GBP),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Wallet),
                dirval!(PaymentMethod = BankRedirect),
                dirval!(PaymentCurrency = GBP),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Wallet),
                dirval!(PaymentMethod = PayLater),
                dirval!(CaptureMethod = Automatic),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Wallet),
                dirval!(PaymentMethod = PayLater),
                dirval!(CaptureMethod = Manual),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = Card),
                dirval!(CaptureMethod = Automatic),
                dirval!(PaymentCurrency = USD),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = Card),
                dirval!(CaptureMethod = Automatic),
                dirval!(PaymentCurrency = GBP),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = Card),
                dirval!(CaptureMethod = Manual),
                dirval!(PaymentCurrency = USD),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = Card),
                dirval!(CaptureMethod = Manual),
                dirval!(PaymentCurrency = GBP),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = BankRedirect),
                dirval!(CaptureMethod = Automatic),
                dirval!(PaymentCurrency = USD),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = BankRedirect),
                dirval!(CaptureMethod = Automatic),
                dirval!(PaymentCurrency = GBP),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = BankRedirect),
                dirval!(CaptureMethod = Manual),
                dirval!(PaymentCurrency = USD),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = BankRedirect),
                dirval!(CaptureMethod = Manual),
                dirval!(PaymentCurrency = GBP),
            ],
        ];

        let mut expected_idx = 0usize;
        while !state_machine.is_finished() {
            let values = ctx
                .iter()
                .flat_map(|c| match c.value {
                    types::CtxValueKind::Assertion(val) => vec![val],
                    types::CtxValueKind::Negation(vals) => vals.iter().collect(),
                })
                .collect::<Vec<&dir::DirValue>>();
            assert_eq!(
                values,
                expected_contexts
                    .get(expected_idx)
                    .expect("Error deriving contexts")
                    .iter()
                    .collect::<Vec<&dir::DirValue>>()
            );
            expected_idx += 1;
            state_machine
                .advance(&mut ctx)
                .expect("State Machine advance");
        }

        assert_eq!(expected_idx, 14);

        let mut ctx_manager = AnalysisContextManager::new(&lowered, &selection_data);
        expected_idx = 0;
        while let Some(ctx) = ctx_manager.advance().expect("Context Manager Context") {
            let values = ctx
                .iter()
                .flat_map(|c| match c.value {
                    types::CtxValueKind::Assertion(val) => vec![val],
                    types::CtxValueKind::Negation(vals) => vals.iter().collect(),
                })
                .collect::<Vec<&dir::DirValue>>();
            assert_eq!(
                values,
                expected_contexts
                    .get(expected_idx)
                    .expect("Error deriving contexts")
                    .iter()
                    .collect::<Vec<&dir::DirValue>>()
            );
            expected_idx += 1;
        }

        assert_eq!(expected_idx, 14);
    }
}

Tests: crates/euclid/src/dssa/state_machine.rs
================================================================================
use super::types::EuclidAnalysable;
use crate::{dssa::types, frontend::dir, types::Metadata};

#[derive(Debug, Clone, serde::Serialize, thiserror::Error)]
#[serde(tag = "type", content = "info", rename_all = "snake_case")]
pub enum StateMachineError {
    #[error("Index out of bounds: {0}")]
    IndexOutOfBounds(&'static str),
}

#[derive(Debug)]
struct ComparisonStateMachine<'a> {
    values: &'a [dir::DirValue],
    logic: &'a dir::DirComparisonLogic,
    metadata: &'a Metadata,
    count: usize,
    ctx_idx: usize,
}

impl<'a> ComparisonStateMachine<'a> {
    #[inline]
    fn is_finished(&self) -> bool {
        self.count + 1 >= self.values.len()
            || matches!(self.logic, dir::DirComparisonLogic::NegativeConjunction)
    }

    #[inline]
    fn advance(&mut self) {
        if let dir::DirComparisonLogic::PositiveDisjunction = self.logic {
            self.count = (self.count + 1) % self.values.len();
        }
    }

    #[inline]
    fn reset(&mut self) {
        self.count = 0;
    }

    #[inline]
    fn put(&self, context: &mut types::ConjunctiveContext<'a>) -> Result<(), StateMachineError> {
        if let dir::DirComparisonLogic::PositiveDisjunction = self.logic {
            *context
                .get_mut(self.ctx_idx)
                .ok_or(StateMachineError::IndexOutOfBounds(
                    "in ComparisonStateMachine while indexing into context",
                ))? = types::ContextValue::assertion(
                self.values
                    .get(self.count)
                    .ok_or(StateMachineError::IndexOutOfBounds(
                        "in ComparisonStateMachine while indexing into values",
                    ))?,
                self.metadata,
            );
        }
        Ok(())
    }

    #[inline]
    fn push(&self, context: &mut types::ConjunctiveContext<'a>) -> Result<(), StateMachineError> {
        match self.logic {
            dir::DirComparisonLogic::PositiveDisjunction => {
                context.push(types::ContextValue::assertion(
                    self.values
                        .get(self.count)
                        .ok_or(StateMachineError::IndexOutOfBounds(
                            "in ComparisonStateMachine while pushing",
                        ))?,
                    self.metadata,
                ));
            }

            dir::DirComparisonLogic::NegativeConjunction => {
                context.push(types::ContextValue::negation(self.values, self.metadata));
            }
        }
        Ok(())
    }
}

#[derive(Debug)]
struct ConditionStateMachine<'a> {
    state_machines: Vec<ComparisonStateMachine<'a>>,
    start_ctx_idx: usize,
}

impl<'a> ConditionStateMachine<'a> {
    fn new(condition: &'a [dir::DirComparison], start_idx: usize) -> Self {
        let mut machines = Vec::<ComparisonStateMachine<'a>>::with_capacity(condition.len());

        let mut machine_idx = start_idx;
        for cond in condition {
            let machine = ComparisonStateMachine {
                values: &cond.values,
                logic: &cond.logic,
                metadata: &cond.metadata,
                count: 0,
                ctx_idx: machine_idx,
            };
            machines.push(machine);
            machine_idx += 1;
        }

        Self {
            state_machines: machines,
            start_ctx_idx: start_idx,
        }
    }

    fn init(&self, context: &mut types::ConjunctiveContext<'a>) -> Result<(), StateMachineError> {
        for machine in &self.state_machines {
            machine.push(context)?;
        }
        Ok(())
    }

    #[inline]
    fn destroy(&self, context: &mut types::ConjunctiveContext<'a>) {
        context.truncate(self.start_ctx_idx);
    }

    #[inline]
    fn is_finished(&self) -> bool {
        !self
            .state_machines
            .iter()
            .any(|machine| !machine.is_finished())
    }

    #[inline]
    fn get_next_ctx_idx(&self) -> usize {
        self.start_ctx_idx + self.state_machines.len()
    }

    fn advance(
        &mut self,
        context: &mut types::ConjunctiveContext<'a>,
    ) -> Result<(), StateMachineError> {
        for machine in self.state_machines.iter_mut().rev() {
            if machine.is_finished() {
                machine.reset();
                machine.put(context)?;
            } else {
                machine.advance();
                machine.put(context)?;
                break;
            }
        }
        Ok(())
    }
}

#[derive(Debug)]
struct IfStmtStateMachine<'a> {
    condition_machine: ConditionStateMachine<'a>,
    nested: Vec<&'a dir::DirIfStatement>,
    nested_idx: usize,
}

impl<'a> IfStmtStateMachine<'a> {
    fn new(stmt: &'a dir::DirIfStatement, ctx_start_idx: usize) -> Self {
        let condition_machine = ConditionStateMachine::new(&stmt.condition, ctx_start_idx);
        let nested: Vec<&'a dir::DirIfStatement> = match &stmt.nested {
            None => Vec::new(),
            Some(nested_stmts) => nested_stmts.iter().collect(),
        };

        Self {
            condition_machine,
            nested,
            nested_idx: 0,
        }
    }

    fn init(
        &self,
        context: &mut types::ConjunctiveContext<'a>,
    ) -> Result<Option<Self>, StateMachineError> {
        self.condition_machine.init(context)?;
        Ok(self
            .nested
            .first()
            .map(|nested| Self::new(nested, self.condition_machine.get_next_ctx_idx())))
    }

    #[inline]
    fn is_finished(&self) -> bool {
        self.nested_idx + 1 >= self.nested.len()
    }

    #[inline]
    fn is_condition_machine_finished(&self) -> bool {
        self.condition_machine.is_finished()
    }

    #[inline]
    fn destroy(&self, context: &mut types::ConjunctiveContext<'a>) {
        self.condition_machine.destroy(context);
    }

    #[inline]
    fn advance_condition_machine(
        &mut self,
        context: &mut types::ConjunctiveContext<'a>,
    ) -> Result<(), StateMachineError> {
        self.condition_machine.advance(context)?;
        Ok(())
    }

    fn advance(&mut self) -> Result<Option<Self>, StateMachineError> {
        if self.nested.is_empty() {
            Ok(None)
        } else {
            self.nested_idx = (self.nested_idx + 1) % self.nested.len();
            Ok(Some(Self::new(
                self.nested
                    .get(self.nested_idx)
                    .ok_or(StateMachineError::IndexOutOfBounds(
                        "in IfStmtStateMachine while advancing",
                    ))?,
                self.condition_machine.get_next_ctx_idx(),
            )))
        }
    }
}

#[derive(Debug)]
struct RuleStateMachine<'a> {
    connector_selection_data: &'a [(dir::DirValue, Metadata)],
    connectors_added: bool,
    if_stmt_machines: Vec<IfStmtStateMachine<'a>>,
    running_stack: Vec<IfStmtStateMachine<'a>>,
}

impl<'a> RuleStateMachine<'a> {
    fn new<O>(
        rule: &'a dir::DirRule<O>,
        connector_selection_data: &'a [(dir::DirValue, Metadata)],
    ) -> Self {
        let mut if_stmt_machines: Vec<IfStmtStateMachine<'a>> =
            Vec::with_capacity(rule.statements.len());

        for stmt in rule.statements.iter().rev() {
            if_stmt_machines.push(IfStmtStateMachine::new(
                stmt,
                connector_selection_data.len(),
            ));
        }

        Self {
            connector_selection_data,
            connectors_added: false,
            if_stmt_machines,
            running_stack: Vec::new(),
        }
    }

    fn is_finished(&self) -> bool {
        self.if_stmt_machines.is_empty() && self.running_stack.is_empty()
    }

    fn init_next(
        &mut self,
        context: &mut types::ConjunctiveContext<'a>,
    ) -> Result<(), StateMachineError> {
        if self.if_stmt_machines.is_empty() || !self.running_stack.is_empty() {
            return Ok(());
        }

        if !self.connectors_added {
            for (dir_val, metadata) in self.connector_selection_data {
                context.push(types::ContextValue::assertion(dir_val, metadata));
            }
            self.connectors_added = true;
        }

        context.truncate(self.connector_selection_data.len());

        if let Some(mut next_running) = self.if_stmt_machines.pop() {
            while let Some(nested_running) = next_running.init(context)? {
                self.running_stack.push(next_running);
                next_running = nested_running;
            }

            self.running_stack.push(next_running);
        }

        Ok(())
    }

    fn advance(
        &mut self,
        context: &mut types::ConjunctiveContext<'a>,
    ) -> Result<(), StateMachineError> {
        let mut condition_machines_finished = true;

        for stmt_machine in self.running_stack.iter_mut().rev() {
            if !stmt_machine.is_condition_machine_finished() {
                condition_machines_finished = false;
                stmt_machine.advance_condition_machine(context)?;
                break;
            } else {
                stmt_machine.advance_condition_machine(context)?;
            }
        }

        if !condition_machines_finished {
            return Ok(());
        }

        let mut maybe_next_running: Option<IfStmtStateMachine<'a>> = None;

        while let Some(last) = self.running_stack.last_mut() {
            if !last.is_finished() {
                maybe_next_running = last.advance()?;
                break;
            } else {
                last.destroy(context);
                self.running_stack.pop();
            }
        }

        if let Some(mut next_running) = maybe_next_running {
            while let Some(nested_running) = next_running.init(context)? {
                self.running_stack.push(next_running);
                next_running = nested_running;
            }

            self.running_stack.push(next_running);
        } else {
            self.init_next(context)?;
        }

        Ok(())
    }
}

#[derive(Debug)]
pub struct RuleContextManager<'a> {
    context: types::ConjunctiveContext<'a>,
    machine: RuleStateMachine<'a>,
    init: bool,
}

impl<'a> RuleContextManager<'a> {
    pub fn new<O>(
        rule: &'a dir::DirRule<O>,
        connector_selection_data: &'a [(dir::DirValue, Metadata)],
    ) -> Self {
        Self {
            context: Vec::new(),
            machine: RuleStateMachine::new(rule, connector_selection_data),
            init: false,
        }
    }

    pub fn advance(&mut self) -> Result<Option<&types::ConjunctiveContext<'a>>, StateMachineError> {
        if !self.init {
            self.init = true;
            self.machine.init_next(&mut self.context)?;
            Ok(Some(&self.context))
        } else if self.machine.is_finished() {
            Ok(None)
        } else {
            self.machine.advance(&mut self.context)?;

            if self.machine.is_finished() {
                Ok(None)
            } else {
                Ok(Some(&self.context))
            }
        }
    }

    pub fn advance_mut(
        &mut self,
    ) -> Result<Option<&mut types::ConjunctiveContext<'a>>, StateMachineError> {
        if !self.init {
            self.init = true;
            self.machine.init_next(&mut self.context)?;
            Ok(Some(&mut self.context))
        } else if self.machine.is_finished() {
            Ok(None)
        } else {
            self.machine.advance(&mut self.context)?;

            if self.machine.is_finished() {
                Ok(None)
            } else {
                Ok(Some(&mut self.context))
            }
        }
    }
}

#[derive(Debug)]
pub struct ProgramStateMachine<'a> {
    rule_machines: Vec<RuleStateMachine<'a>>,
    current_rule_machine: Option<RuleStateMachine<'a>>,
    is_init: bool,
}

impl<'a> ProgramStateMachine<'a> {
    pub fn new<O>(
        program: &'a dir::DirProgram<O>,
        connector_selection_data: &'a [Vec<(dir::DirValue, Metadata)>],
    ) -> Self {
        let mut rule_machines: Vec<RuleStateMachine<'a>> = program
            .rules
            .iter()
            .zip(connector_selection_data.iter())
            .rev()
            .map(|(rule, connector_selection_data)| {
                RuleStateMachine::new(rule, connector_selection_data)
            })
            .collect();

        Self {
            current_rule_machine: rule_machines.pop(),
            rule_machines,
            is_init: false,
        }
    }

    pub fn is_finished(&self) -> bool {
        self.current_rule_machine
            .as_ref()
            .is_none_or(|rsm| rsm.is_finished())
            && self.rule_machines.is_empty()
    }

    pub fn init(
        &mut self,
        context: &mut types::ConjunctiveContext<'a>,
    ) -> Result<(), StateMachineError> {
        if !self.is_init {
            if let Some(rsm) = self.current_rule_machine.as_mut() {
                rsm.init_next(context)?;
            }
            self.is_init = true;
        }

        Ok(())
    }

    pub fn advance(
        &mut self,
        context: &mut types::ConjunctiveContext<'a>,
    ) -> Result<(), StateMachineError> {
        if self
            .current_rule_machine
            .as_ref()
            .is_none_or(|rsm| rsm.is_finished())
        {
            self.current_rule_machine = self.rule_machines.pop();
            context.clear();
            if let Some(rsm) = self.current_rule_machine.as_mut() {
                rsm.init_next(context)?;
            }
        } else if let Some(rsm) = self.current_rule_machine.as_mut() {
            rsm.advance(context)?;
        }

        Ok(())
    }
}

pub struct AnalysisContextManager<'a> {
    context: types::ConjunctiveContext<'a>,
    machine: ProgramStateMachine<'a>,
    init: bool,
}

impl<'a> AnalysisContextManager<'a> {
    pub fn new<O>(
        program: &'a dir::DirProgram<O>,
        connector_selection_data: &'a [Vec<(dir::DirValue, Metadata)>],
    ) -> Self {
        let machine = ProgramStateMachine::new(program, connector_selection_data);
        let context: types::ConjunctiveContext<'a> = Vec::new();

        Self {
            context,
            machine,
            init: false,
        }
    }

    pub fn advance(&mut self) -> Result<Option<&types::ConjunctiveContext<'a>>, StateMachineError> {
        if !self.init {
            self.init = true;
            self.machine.init(&mut self.context)?;
            Ok(Some(&self.context))
        } else if self.machine.is_finished() {
            Ok(None)
        } else {
            self.machine.advance(&mut self.context)?;

            if self.machine.is_finished() {
                Ok(None)
            } else {
                Ok(Some(&self.context))
            }
        }
    }
}

pub fn make_connector_selection_data<O: EuclidAnalysable>(
    program: &dir::DirProgram<O>,
) -> Vec<Vec<(dir::DirValue, Metadata)>> {
    program
        .rules
        .iter()
        .map(|rule| {
            rule.connector_selection
                .get_dir_value_for_analysis(rule.name.clone())
        })
        .collect()
}

#[cfg(all(test, feature = "ast_parser"))]
mod tests {
    use super::*;
    use crate::{dirval, frontend::ast, types::DummyOutput};

    #[test]
    fn test_correct_contexts() {
        let program_str = r#"
            default: ["stripe", "adyen"]

            stripe_first: ["stripe", "adyen"]
            {
                payment_method = wallet {
                    payment_method = (card, bank_redirect) {
                        currency = USD
                        currency = GBP
                    }

                    payment_method = pay_later {
                        capture_method = automatic
                        capture_method = manual
                    }
                }

                payment_method = card {
                    payment_method = (card, bank_redirect) & capture_method = (automatic, manual) {
                        currency = (USD, GBP)
                    }
                }
            }
        "#;
        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let lowered = ast::lowering::lower_program(program).expect("Lowering");

        let selection_data = make_connector_selection_data(&lowered);
        let mut state_machine = ProgramStateMachine::new(&lowered, &selection_data);
        let mut ctx: types::ConjunctiveContext<'_> = Vec::new();
        state_machine.init(&mut ctx).expect("State machine init");

        let expected_contexts: Vec<Vec<dir::DirValue>> = vec![
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Wallet),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentCurrency = USD),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Wallet),
                dirval!(PaymentMethod = BankRedirect),
                dirval!(PaymentCurrency = USD),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Wallet),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentCurrency = GBP),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Wallet),
                dirval!(PaymentMethod = BankRedirect),
                dirval!(PaymentCurrency = GBP),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Wallet),
                dirval!(PaymentMethod = PayLater),
                dirval!(CaptureMethod = Automatic),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Wallet),
                dirval!(PaymentMethod = PayLater),
                dirval!(CaptureMethod = Manual),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = Card),
                dirval!(CaptureMethod = Automatic),
                dirval!(PaymentCurrency = USD),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = Card),
                dirval!(CaptureMethod = Automatic),
                dirval!(PaymentCurrency = GBP),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = Card),
                dirval!(CaptureMethod = Manual),
                dirval!(PaymentCurrency = USD),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = Card),
                dirval!(CaptureMethod = Manual),
                dirval!(PaymentCurrency = GBP),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = BankRedirect),
                dirval!(CaptureMethod = Automatic),
                dirval!(PaymentCurrency = USD),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = BankRedirect),
                dirval!(CaptureMethod = Automatic),
                dirval!(PaymentCurrency = GBP),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = BankRedirect),
                dirval!(CaptureMethod = Manual),
                dirval!(PaymentCurrency = USD),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = BankRedirect),
                dirval!(CaptureMethod = Manual),
                dirval!(PaymentCurrency = GBP),
            ],
        ];

        let mut expected_idx = 0usize;
        while !state_machine.is_finished() {
            let values = ctx
                .iter()
                .flat_map(|c| match c.value {
                    types::CtxValueKind::Assertion(val) => vec![val],
                    types::CtxValueKind::Negation(vals) => vals.iter().collect(),
                })
                .collect::<Vec<&dir::DirValue>>();
            assert_eq!(
                values,
                expected_contexts
                    .get(expected_idx)
                    .expect("Error deriving contexts")
                    .iter()
                    .collect::<Vec<&dir::DirValue>>()
            );
            expected_idx += 1;
            state_machine
                .advance(&mut ctx)
                .expect("State Machine advance");
        }

        assert_eq!(expected_idx, 14);

        let mut ctx_manager = AnalysisContextManager::new(&lowered, &selection_data);
        expected_idx = 0;
        while let Some(ctx) = ctx_manager.advance().expect("Context Manager Context") {
            let values = ctx
                .iter()
                .flat_map(|c| match c.value {
                    types::CtxValueKind::Assertion(val) => vec![val],
                    types::CtxValueKind::Negation(vals) => vals.iter().collect(),
                })
                .collect::<Vec<&dir::DirValue>>();
            assert_eq!(
                values,
                expected_contexts
                    .get(expected_idx)
                    .expect("Error deriving contexts")
                    .iter()
                    .collect::<Vec<&dir::DirValue>>()
            );
            expected_idx += 1;
        }

        assert_eq!(expected_idx, 14);
    }
}