Repository: juspay/hyperswitch
File: crates/api_models/src/routing.rs (Rust)

Code before:
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
#[serde(
    tag = "type",
    content = "data",
    rename_all = "snake_case",
    try_from = "RoutingAlgorithmSerde"
)]
pub enum StaticRoutingAlgorithm {
    Single(Box<RoutableConnectorChoice>),
    Priority(Vec<RoutableConnectorChoice>),
    VolumeSplit(Vec<ConnectorVolumeSplit>),
    #[schema(value_type=ProgramConnectorSelection)]
    Advanced(ast::Program<ConnectorSelection>),
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
#[serde(tag = "type", content = "data", rename_all = "snake_case")]
pub enum RoutingAlgorithmSerde {
    Single(Box<RoutableConnectorChoice>),
    Priority(Vec<RoutableConnectorChoice>),
    VolumeSplit(Vec<ConnectorVolumeSplit>),
    Advanced(ast::Program<ConnectorSelection>),
}

impl TryFrom<RoutingAlgorithmSerde> for StaticRoutingAlgorithm {
    type Error = error_stack::Report<ParsingError>;

    fn try_from(value: RoutingAlgorithmSerde) -> Result<Self, Self::Error> {
        match &value {
            RoutingAlgorithmSerde::Priority(i) if i.is_empty() => {
                Err(ParsingError::StructParseFailure(
                    "Connectors list can't be empty for Priority Algorithm",
                ))?
            }
            RoutingAlgorithmSerde::VolumeSplit(i) if i.is_empty() => {
                Err(ParsingError::StructParseFailure(
                    "Connectors list can't be empty for Volume split Algorithm",
                ))?
            }
            _ => {}
        };
        Ok(match value {
            RoutingAlgorithmSerde::Single(i) => Self::Single(i),
            RoutingAlgorithmSerde::Priority(i) => Self::Priority(i),
            RoutingAlgorithmSerde::VolumeSplit(i) => Self::VolumeSplit(i),
            RoutingAlgorithmSerde::Advanced(i) => Self::Advanced(i),
        })
    }
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema, PartialEq)]

Review comment (refactor):
move this under Advanced(Program<ConnectorSelection>), it already handles the priority and volume split. so in future you can easily move this to decision engine. 
 

Code after:
    Dynamic(DynamicRoutingAlgorithm),
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
#[serde(untagged)]
pub enum DynamicRoutingAlgorithm {
    EliminationBasedAlgorithm(EliminationRoutingConfig),
    SuccessBasedAlgorithm(SuccessBasedRoutingConfig),
    ContractBasedAlgorithm(ContractBasedRoutingConfig),
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
#[serde(
    tag = "type",
    content = "data",
    rename_all = "snake_case",
    try_from = "RoutingAlgorithmSerde"
)]
pub enum StaticRoutingAlgorithm {
    Single(Box<RoutableConnectorChoice>),
    Priority(Vec<RoutableConnectorChoice>),
    VolumeSplit(Vec<ConnectorVolumeSplit>),
    #[schema(value_type=ProgramConnectorSelection)]
    Advanced(Program<ConnectorSelection>),
    #[schema(value_type=ProgramThreeDsDecisionRule)]
    ThreeDsDecisionRule(Program<ThreeDSDecisionRule>),
}

#[derive(Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct ProgramThreeDsDecisionRule {
    pub default_selection: ThreeDSDecisionRule,
    #[schema(value_type = RuleThreeDsDecisionRule)]
    pub rules: Vec<ast::Rule<ThreeDSDecisionRule>>,
    #[schema(value_type = HashMap<String, serde_json::Value>)]
    pub metadata: std::collections::HashMap<String, serde_json::Value>,
}

#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct RuleThreeDsDecisionRule {
    pub name: String,
    pub connector_selection: ThreeDSDecision,
    #[schema(value_type = Vec<IfStatement>)]
    pub statements: Vec<ast::IfStatement>,
}

impl StaticRoutingAlgorithm {
    pub fn should_validate_connectors_in_routing_config(&self) -> bool {
        match self {
            Self::Single(_) | Self::Priority(_) | Self::VolumeSplit(_) | Self::Advanced(_) => true,