open_feature_flagd/resolver/in_process/targeting/
mod.rs

1use anyhow::Result;
2use datalogic_rs::DataLogic;
3use datalogic_rs::{DataValue, FromJson};
4use open_feature::{EvaluationContext, EvaluationContextFieldValue};
5use serde_json::Value;
6use std::sync::{Arc, Mutex};
7
8mod fractional;
9mod semver;
10
11use fractional::fractional_fn;
12use semver::sem_ver_fn;
13
14pub struct Operator {
15    // Wrap DataLogic in Arc<Mutex<_>> to make it thread-safe
16    logic: Arc<Mutex<DataLogic>>,
17}
18
19impl Operator {
20    pub fn new() -> Self {
21        // Create a new DataLogic instance
22        let mut logic = DataLogic::new();
23
24        // Register simple operators
25        logic.register_simple_operator("fractional", fractional_fn);
26        logic.register_simple_operator("sem_ver", sem_ver_fn);
27
28        Operator {
29            logic: Arc::new(Mutex::new(logic)),
30        }
31    }
32
33    pub fn apply(
34        &self,
35        flag_key: &str,
36        targeting_rule: &str,
37        ctx: &EvaluationContext,
38    ) -> Result<Option<String>> {
39        // Parse the rule from JSON string
40        let rule_value: Value = serde_json::from_str(targeting_rule)?;
41
42        // Lock the mutex to access DataLogic
43        let logic_instance = self
44            .logic
45            .lock()
46            .map_err(|_| anyhow::anyhow!("Failed to acquire lock"))?;
47
48        // Parse the rule
49        let logic = logic_instance.parse_logic_json(&rule_value, None)?;
50
51        // Build context data directly as DataValue
52        let context_data = self.build_datavalue_context(flag_key, ctx, &logic_instance);
53
54        // Evaluate using DataLogic
55        match logic_instance.evaluate(&logic, &context_data) {
56            Ok(result) => {
57                // Convert result to Option<String>
58                match result {
59                    DataValue::String(s) => Ok(Some(s.to_string())),
60                    DataValue::Null => Ok(None),
61                    _ => Ok(Some(format!("{}", result))),
62                }
63            }
64            Err(e) => {
65                // Log and return None on error
66                tracing::debug!("DataLogic evaluation error: {:?}", e);
67                Ok(None)
68            }
69        }
70    }
71
72    fn build_datavalue_context<'a>(
73        &self,
74        flag_key: &str,
75        ctx: &EvaluationContext,
76        logic: &'a DataLogic,
77    ) -> DataValue<'a> {
78        // Create a JSON object for our context
79        let mut root = serde_json::Map::new();
80
81        // Add targeting key if present
82        if let Some(targeting_key) = &ctx.targeting_key {
83            root.insert(
84                "targetingKey".to_string(),
85                serde_json::Value::String(targeting_key.clone()),
86            );
87        }
88
89        // Add flagd metadata
90        let timestamp = std::time::SystemTime::now()
91            .duration_since(std::time::UNIX_EPOCH)
92            .unwrap()
93            .as_secs();
94
95        // Create flagd object
96        let mut flagd_props = serde_json::Map::new();
97        flagd_props.insert(
98            "flagKey".to_string(),
99            serde_json::Value::String(flag_key.to_string()),
100        );
101        flagd_props.insert(
102            "timestamp".to_string(),
103            serde_json::Value::Number(serde_json::Number::from(timestamp)),
104        );
105
106        // Add flagd object to main object
107        root.insert("$flagd".to_string(), serde_json::Value::Object(flagd_props));
108
109        // Add custom fields
110        for (key, value) in &ctx.custom_fields {
111            root.insert(key.clone(), self.evaluation_context_value_to_json(value));
112        }
113
114        // Create the JSON object
115        let json_value = serde_json::Value::Object(root);
116
117        // Convert JSON to DataValue using the arena
118        DataValue::from_json(&json_value, logic.arena())
119    }
120
121    // Helper to convert EvaluationContextFieldValue to serde_json::Value
122    fn evaluation_context_value_to_json(
123        &self,
124        value: &EvaluationContextFieldValue,
125    ) -> serde_json::Value {
126        match value {
127            EvaluationContextFieldValue::String(s) => serde_json::Value::String(s.clone()),
128            EvaluationContextFieldValue::Bool(b) => serde_json::Value::Bool(*b),
129            EvaluationContextFieldValue::Int(i) => {
130                serde_json::Value::Number(serde_json::Number::from(*i))
131            }
132            EvaluationContextFieldValue::Float(f) => {
133                if let Some(num) = serde_json::Number::from_f64(*f) {
134                    serde_json::Value::Number(num)
135                } else {
136                    serde_json::Value::Null
137                }
138            }
139            EvaluationContextFieldValue::DateTime(dt) => serde_json::Value::String(dt.to_string()),
140            EvaluationContextFieldValue::Struct(s) => serde_json::Value::String(format!("{:?}", s)),
141        }
142    }
143}