Bug: refactor: add result type for Program



### Description
This PR refactors the conversion from ast::Program<ConnectorSelection> to internal Program by introducing a fallible TryFrom implementation instead of an infallible From. This enables proper error propagation and logging during DSL conversion failures.

Supporting conversion functions (convert_rule, convert_if_stmt, convert_comparison, convert_value) now return RoutingResult<T> instead of plain values, allowing fine-grained error handling throughout the conversion pipeline.

Also, the core/routing.rs logic has been updated to handle these errors gracefully by logging and skipping invalid programs during routing algorithm creation.
diff --git a/crates/router/src/core/payments/routing/utils.rs b/crates/router/src/core/payments/routing/utils.rs
index e82ab1d8bee..01d066e5cf2 100644
--- a/crates/router/src/core/payments/routing/utils.rs
+++ b/crates/router/src/core/payments/routing/utils.rs
@@ -1,6 +1,9 @@
 use std::collections::{HashMap, HashSet};
 
-use api_models::routing as api_routing;
+use api_models::{
+    routing as api_routing,
+    routing::{ConnectorSelection, RoutableConnectorChoice},
+};
 use async_trait::async_trait;
 use common_utils::id_type;
 use diesel_models::{enums, routing_algorithm};
@@ -712,48 +715,70 @@ impl From<RoutingAlgorithmRecord> for routing_algorithm::RoutingProfileMetadata
         }
     }
 }
-use api_models::routing::{ConnectorSelection, RoutableConnectorChoice};
-impl From<ast::Program<ConnectorSelection>> for Program {
-    fn from(p: ast::Program<ConnectorSelection>) -> Self {
-        Self {
+
+impl TryFrom<ast::Program<ConnectorSelection>> for Program {
+    type Error = error_stack::Report<errors::RoutingError>;
+
+    fn try_from(p: ast::Program<ConnectorSelection>) -> Result<Self, Self::Error> {
+        let rules = p
+            .rules
+            .into_iter()
+            .map(convert_rule)
+            .collect::<Result<Vec<_>, _>>()?;
+
+        Ok(Self {
             globals: HashMap::new(),
             default_selection: convert_output(p.default_selection),
-            rules: p.rules.into_iter().map(convert_rule).collect(),
+            rules,
             metadata: Some(p.metadata),
-        }
+        })
     }
 }
 
-fn convert_rule(rule: ast::Rule<ConnectorSelection>) -> Rule {
+fn convert_rule(rule: ast::Rule<ConnectorSelection>) -> RoutingResult<Rule> {
     let routing_type = match &rule.connector_selection {
         ConnectorSelection::Priority(_) => RoutingType::Priority,
         ConnectorSelection::VolumeSplit(_) => RoutingType::VolumeSplit,
     };
 
-    Rule {
+    Ok(Rule {
         name: rule.name,
         routing_type,
         output: convert_output(rule.connector_selection),
-        statements: rule.statements.into_iter().map(convert_if_stmt).collect(),
-    }
+        statements: rule
+            .statements
+            .into_iter()
+            .map(convert_if_stmt)
+            .collect::<RoutingResult<Vec<IfStatement>>>()?,
+    })
 }
 
-fn convert_if_stmt(stmt: ast::IfStatement) -> IfStatement {
-    IfStatement {
-        condition: stmt.condition.into_iter().map(convert_comparison).collect(),
+fn convert_if_stmt(stmt: ast::IfStatement) -> RoutingResult<IfStatement> {
+    Ok(IfStatement {
+        condition: stmt
+            .condition
+            .into_iter()
+            .map(convert_comparison)
+            .collect::<RoutingResult<Vec<Comparison>>>()?,
+
         nested: stmt
             .nested
-            .map(|v| v.into_iter().map(convert_if_stmt).collect()),
-    }
+            .map(|v| {
+                v.into_iter()
+                    .map(convert_if_stmt)
+                    .collect::<RoutingResult<Vec<IfStatement>>>()
+            })
+            .transpose()?,
+    })
 }
 
-fn convert_comparison(c: ast::Comparison) -> Comparison {
-    Comparison {
+fn convert_comparison(c: ast::Comparison) -> RoutingResult<Comparison> {
+    Ok(Comparison {
         lhs: c.lhs,
         comparison: convert_comparison_type(c.comparison),
-        value: convert_value(c.value),
+        value: convert_value(c.value)?,
         metadata: c.metadata,
-    }
+    })
 }
 
 fn convert_comparison_type(ct: ast::ComparisonType) -> ComparisonType {
@@ -767,18 +792,21 @@ fn convert_comparison_type(ct: ast::ComparisonType) -> ComparisonType {
     }
 }
 
-#[allow(clippy::unimplemented)]
-fn convert_value(v: ast::ValueType) -> ValueType {
+fn convert_value(v: ast::ValueType) -> RoutingResult<ValueType> {
     use ast::ValueType::*;
     match v {
-        Number(n) => ValueType::Number(n.get_amount_as_i64().try_into().unwrap_or_default()),
-        EnumVariant(e) => ValueType::EnumVariant(e),
-        MetadataVariant(m) => ValueType::MetadataVariant(MetadataValue {
+        Number(n) => Ok(ValueType::Number(
+            n.get_amount_as_i64().try_into().unwrap_or_default(),
+        )),
+        EnumVariant(e) => Ok(ValueType::EnumVariant(e)),
+        MetadataVariant(m) => Ok(ValueType::MetadataVariant(MetadataValue {
             key: m.key,
             value: m.value,
-        }),
-        StrValue(s) => ValueType::StrValue(s),
-        _ => unimplemented!(), // GlobalRef(r) => ValueType::GlobalRef(r),
+        })),
+        StrValue(s) => Ok(ValueType::StrValue(s)),
+        _ => Err(error_stack::Report::new(
+            errors::RoutingError::InvalidRoutingAlgorithmStructure,
+        )),
     }
 }
 
diff --git a/crates/router/src/core/routing.rs b/crates/router/src/core/routing.rs
index 1811f43d51e..e8e87c895d5 100644
--- a/crates/router/src/core/routing.rs
+++ b/crates/router/src/core/routing.rs
@@ -340,26 +340,32 @@ pub async fn create_routing_algorithm_under_profile(
     let mut decision_engine_routing_id: Option<String> = None;
 
     if let Some(EuclidAlgorithm::Advanced(program)) = request.algorithm.clone() {
-        let internal_program: Program = program.into();
-        let routing_rule = RoutingRule {
-            name: name.clone(),
-            description: Some(description.clone()),
-            created_by: profile_id.get_string_repr().to_string(),
-            algorithm: internal_program,
-            metadata: Some(RoutingMetadata {
-                kind: algorithm.get_kind().foreign_into(),
-                algorithm_for: transaction_type.to_owned(),
-            }),
-        };
-
-        decision_engine_routing_id = create_de_euclid_routing_algo(&state, &routing_rule)
-            .await
-            .map_err(|e| {
+        match program.try_into() {
+            Ok(internal_program) => {
+                let routing_rule = RoutingRule {
+                    name: name.clone(),
+                    description: Some(description.clone()),
+                    created_by: profile_id.get_string_repr().to_string(),
+                    algorithm: internal_program,
+                    metadata: Some(RoutingMetadata {
+                        kind: algorithm.get_kind().foreign_into(),
+                        algorithm_for: transaction_type.to_owned(),
+                    }),
+                };
+
+                decision_engine_routing_id = create_de_euclid_routing_algo(&state, &routing_rule)
+                    .await
+                    .map_err(|e| {
+                        // errors are ignored as this is just for diff checking as of now (optional flow).
+                        logger::error!(decision_engine_error=?e,decision_engine_euclid_request=?routing_rule, "failed to create rule in decision_engine");
+                    })
+                    .ok();
+            }
+            Err(e) => {
                 // errors are ignored as this is just for diff checking as of now (optional flow).
                 logger::error!(decision_engine_error=?e, "decision_engine_euclid");
-                logger::debug!(decision_engine_request=?routing_rule, "decision_engine_euclid");
-            })
-            .ok();
+            }
+        };
     }
 
     if decision_engine_routing_id.is_some() {

## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [ ] Bugfix
- [ ] New feature
- [ ] Enhancement
- [x] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description

This PR refactors the conversion from `ast::Program<ConnectorSelection>` to internal `Program` by introducing a fallible `TryFrom` implementation instead of an infallible `From`. This enables proper error propagation and logging during DSL conversion failures. 

Supporting conversion functions (`convert_rule`, `convert_if_stmt`, `convert_comparison`, `convert_value`) now return `RoutingResult<T>` instead of plain values, allowing fine-grained error handling throughout the conversion pipeline.

Also, the `core/routing.rs` logic has been updated to handle these errors gracefully by logging and skipping invalid programs during routing algorithm creation.

---

## Outcomes

- Adds **robust error handling** for AST-to-internal structure conversion.
- Improves **diagnostic logging** on conversion failures.
- Prevents invalid DSL programs from entering routing configuration.
- Adheres to idiomatic Rust usage with `TryFrom` and `Result`.

---

## Diff Hunk Explanation

### `crates/router/src/core/payments/routing/utils.rs`

- Changed `impl From<ast::Program<ConnectorSelection>> for Program` to `TryFrom`, enabling `Result`-based error handling.
- Functions `convert_rule`, `convert_if_stmt`, `convert_comparison`, and `convert_value` were updated to return `RoutingResult<T>`.
- Explicit handling of unsupported `ValueType` variants in `convert_value` with an appropriate `RoutingError`.

### `crates/router/src/core/routing.rs`

- Replaced direct `.into()` conversion with a `match` on `try_into()` to handle errors without panicking.
- Logs any conversion errors using `logger::error!` and `logger::debug!`.
- Ensures that the creation of routing algorithms is skipped if DSL conversion fails.


### Additional Changes

- [ ] This PR modifies the API contract
- [ ] This PR modifies the database schema
- [ ] This PR modifies application configuration/environment variables


## How did you test it?
<!--
Did you write an integration/unit/API test to verify the code changes?
Or did you test this change manually (provide relevant screenshots)?
-->
```
curl --location 'http://127.0.0.1:8080/routing' \
--header 'Content-Type: application/json' \
--header 'api-key: dev_IpYFqCd4DHf7x4ER1GBQrW0mHOZzam3pYyuP2tSRb6q7qvTw7jvEjF7UzjwFPXWu' \
--data '
{
    "name": "Priority rule with fallback",
    "description": "It is my ADVANCED config",
    "profile_id": "pro_INSNcGnV7dSldWXpW1GE",
    "algorithm": {
        "type": "advanced", 
        "data": {
            "defaultSelection": {
                "type": "priority",
                "data": [
                    {
                        "connector": "stripe",
                        "merchant_connector_id": "mca_gtMC3BG1m6YTX0j8hwUU"
                    }
                ]
            },
            "rules": [
                {
                    "name": "cybersource first",
                    "connectorSelection": {
                        "type": "priority",
                        "data": [
                            {
                                "connector": "stripe",
                                "merchant_connector_id": "mca_gtMC3BG1m6YTX0j8hwUU"
                            }
                        ]
                    },
                    "statements": [
                        {
                            "condition": [
                                {
                                    "lhs": "billing_country",
                                    "comparison": "equal",
                                    "value": {
                                        "type": "enum_variant",
                                        "value": "Netherlands"
                                    },
                                    "metadata": {}
                                },
                                {
                                "lhs": "amount",
                                "comparison": "greater_than",
                                "value": {
                                    "type": "number_array",
                                    "value": [1000,2000]
                                },
                                "metadata": {}
                            }
                            ],
                            "nested": null
                        }
                    ]
                }
            ],
            "metadata": {}
        }
    }
}
'
```
Response:
```
{
    "id": "routing_Ti6HxelZEfdijoVSPNXA",
    "profile_id": "pro_INSNcGnV7dSldWXpW1GE",
    "name": "Priority rule with fallback",
    "kind": "advanced",
    "description": "It is my ADVANCED config",
    "created_at": 1748515295,
    "modified_at": 1748515295,
    "algorithm_for": "payment",
    "decision_engine_routing_id": null
}
```
The log should have this error type.
<img width="1453" alt="Screenshot 2025-05-29 at 16 12 03" src="https://github.com/user-attachments/assets/a9001b64-e4ef-44ee-bc9d-63b66552971f" />


## Checklist
<!-- Put an `x` in the boxes that apply -->

- [ ] I formatted the code `cargo +nightly fmt --all`
- [ ] I addressed lints thrown by `cargo clippy`
- [ ] I reviewed the submitted code
- [ ] I added unit tests for my changes where possible
