|
1 | 1 | use datadog_ffe::rules_based::{ |
2 | | - self as ffe, AssignmentReason, AssignmentValue, Configuration, EvaluationContext, |
3 | | - EvaluationError, UniversalFlagConfig, |
| 2 | + self as ffe, Attribute, AssignmentReason, AssignmentValue, Configuration, EvaluationContext, |
| 3 | + EvaluationError, Str, UniversalFlagConfig, |
4 | 4 | }; |
5 | | -use serde_json::Value; |
6 | 5 | use std::collections::HashMap; |
7 | 6 | use std::ffi::{c_char, CStr, CString}; |
8 | 7 | use std::ptr; |
9 | | -use std::sync::Mutex; |
| 8 | +use std::sync::{Arc, Mutex}; |
10 | 9 |
|
11 | 10 | lazy_static::lazy_static! { |
12 | 11 | static ref FFE_CONFIG: Mutex<Option<Configuration>> = Mutex::new(None); |
@@ -103,12 +102,9 @@ pub extern "C" fn ddog_ffe_evaluate( |
103 | 102 | // Parse context from JSON |
104 | 103 | let context = if !context_json.is_null() && context_json_len > 0 { |
105 | 104 | let bytes = unsafe { std::slice::from_raw_parts(context_json, context_json_len) }; |
106 | | - match serde_json::from_slice::<EvaluationContext>(bytes) { |
107 | | - Ok(ctx) => ctx, |
108 | | - Err(_) => EvaluationContext::default(), |
109 | | - } |
| 105 | + parse_evaluation_context(bytes) |
110 | 106 | } else { |
111 | | - EvaluationContext::default() |
| 107 | + EvaluationContext::new(None, Arc::new(HashMap::new())) |
112 | 108 | }; |
113 | 109 |
|
114 | 110 | let guard = match FFE_CONFIG.lock() { |
@@ -281,16 +277,47 @@ fn assignment_value_to_json(value: &AssignmentValue) -> String { |
281 | 277 | AssignmentValue::String(s) => serde_json::to_string(s.as_str()).unwrap_or_default(), |
282 | 278 | AssignmentValue::Integer(i) => i.to_string(), |
283 | 279 | AssignmentValue::Float(f) => { |
284 | | - // Ensure floats are serialized consistently |
285 | | - if f.fract() == 0.0 && f.abs() < i64::MAX as f64 { |
286 | | - format!("{f:.1}") |
287 | | - } else { |
288 | | - serde_json::Number::from_f64(*f) |
289 | | - .map(|n| n.to_string()) |
290 | | - .unwrap_or_else(|| f.to_string()) |
291 | | - } |
| 280 | + serde_json::Number::from_f64(*f) |
| 281 | + .map(|n| n.to_string()) |
| 282 | + .unwrap_or_else(|| f.to_string()) |
292 | 283 | } |
293 | 284 | AssignmentValue::Boolean(b) => b.to_string(), |
294 | | - AssignmentValue::Json(v) => serde_json::to_string(v).unwrap_or_default(), |
| 285 | + AssignmentValue::Json { raw, .. } => raw.get().to_string(), |
| 286 | + } |
| 287 | +} |
| 288 | + |
| 289 | +/// Parse a JSON evaluation context into an EvaluationContext. |
| 290 | +/// Expected format: {"targeting_key": "...", "attributes": {"key": value, ...}} |
| 291 | +fn parse_evaluation_context(bytes: &[u8]) -> EvaluationContext { |
| 292 | + let parsed: serde_json::Value = match serde_json::from_slice(bytes) { |
| 293 | + Ok(v) => v, |
| 294 | + Err(_) => return EvaluationContext::new(None, Arc::new(HashMap::new())), |
| 295 | + }; |
| 296 | + |
| 297 | + let targeting_key = parsed |
| 298 | + .get("targeting_key") |
| 299 | + .and_then(|v| v.as_str()) |
| 300 | + .map(Str::from); |
| 301 | + |
| 302 | + let mut attributes = HashMap::new(); |
| 303 | + if let Some(attrs) = parsed.get("attributes").and_then(|v| v.as_object()) { |
| 304 | + for (k, v) in attrs { |
| 305 | + let attr = match v { |
| 306 | + serde_json::Value::String(s) => Attribute::from(s.as_str()), |
| 307 | + serde_json::Value::Number(n) => { |
| 308 | + if let Some(f) = n.as_f64() { |
| 309 | + Attribute::from(f) |
| 310 | + } else { |
| 311 | + continue; |
| 312 | + } |
| 313 | + } |
| 314 | + serde_json::Value::Bool(b) => Attribute::from(*b), |
| 315 | + serde_json::Value::Null => continue, |
| 316 | + _ => continue, |
| 317 | + }; |
| 318 | + attributes.insert(Str::from(k.as_str()), attr); |
| 319 | + } |
295 | 320 | } |
| 321 | + |
| 322 | + EvaluationContext::new(targeting_key, Arc::new(attributes)) |
296 | 323 | } |
0 commit comments