open_feature_flagd/resolver/in_process/targeting/
mod.rs1use 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 logic: Arc<Mutex<DataLogic>>,
17}
18
19impl Operator {
20 pub fn new() -> Self {
21 let mut logic = DataLogic::new();
23
24 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 let rule_value: Value = serde_json::from_str(targeting_rule)?;
41
42 let logic_instance = self
44 .logic
45 .lock()
46 .map_err(|_| anyhow::anyhow!("Failed to acquire lock"))?;
47
48 let logic = logic_instance.parse_logic_json(&rule_value, None)?;
50
51 let context_data = self.build_datavalue_context(flag_key, ctx, &logic_instance);
53
54 match logic_instance.evaluate(&logic, &context_data) {
56 Ok(result) => {
57 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 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 let mut root = serde_json::Map::new();
80
81 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 let timestamp = std::time::SystemTime::now()
91 .duration_since(std::time::UNIX_EPOCH)
92 .unwrap()
93 .as_secs();
94
95 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 root.insert("$flagd".to_string(), serde_json::Value::Object(flagd_props));
108
109 for (key, value) in &ctx.custom_fields {
111 root.insert(key.clone(), self.evaluation_context_value_to_json(value));
112 }
113
114 let json_value = serde_json::Value::Object(root);
116
117 DataValue::from_json(&json_value, logic.arena())
119 }
120
121 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}