flagd_evaluation_engine/model/
flag_parser.rs1use super::feature_flag::FeatureFlag;
2use super::feature_flag::ParsingResult;
3use crate::error::FlagdEvaluationError;
4use serde_json::{Map, Value};
5use std::collections::HashMap;
6
7pub struct FlagParser;
8
9impl FlagParser {
10 pub fn parse_string(configuration: &str) -> Result<ParsingResult, FlagdEvaluationError> {
11 let mut value: Value = serde_json::from_str(configuration)?;
12 Self::transpose_evaluator_refs(&mut value)?;
13
14 let obj = value
15 .as_object()
16 .ok_or_else(|| FlagdEvaluationError::Parse("Invalid JSON structure".to_string()))?;
17
18 let flags = obj
19 .get("flags")
20 .and_then(|v| v.as_object())
21 .ok_or_else(|| {
22 FlagdEvaluationError::Parse(
23 "No flag configurations found in the payload".to_string(),
24 )
25 })?;
26
27 let flag_set_metadata = obj
28 .get("metadata")
29 .and_then(|v| v.as_object())
30 .map(Self::convert_map_to_hashmap)
31 .unwrap_or_default();
32
33 let mut flag_map = HashMap::new();
34 for (key, value) in flags {
35 let flag: FeatureFlag = serde_json::from_value(value.clone())?;
36 flag_map.insert(key.clone(), flag);
37 }
38
39 Ok(ParsingResult {
40 flags: flag_map,
41 flag_set_metadata,
42 })
43 }
44
45 fn transpose_evaluator_refs(configuration: &mut Value) -> Result<(), FlagdEvaluationError> {
46 let evaluators = configuration
47 .get("$evaluators")
48 .and_then(Value::as_object)
49 .cloned()
50 .unwrap_or_default();
51
52 let Some(flags) = configuration
53 .get_mut("flags")
54 .and_then(Value::as_object_mut)
55 else {
56 return Ok(());
57 };
58
59 for flag in flags.values_mut() {
60 if let Some(targeting) = flag.get_mut("targeting") {
61 Self::resolve_refs(targeting, &evaluators, &mut Vec::new())?;
62 }
63 }
64
65 Ok(())
66 }
67
68 fn resolve_refs(
69 value: &mut Value,
70 evaluators: &Map<String, Value>,
71 stack: &mut Vec<String>,
72 ) -> Result<(), FlagdEvaluationError> {
73 match value {
74 Value::Object(obj) => {
75 if obj.len() == 1
76 && let Some(ref_name) = obj.get("$ref").and_then(Value::as_str)
77 {
78 if stack.iter().any(|name| name == ref_name) {
79 return Err(FlagdEvaluationError::Parse(format!(
80 "Circular evaluator reference detected: {}",
81 ref_name
82 )));
83 }
84
85 let mut replacement = evaluators.get(ref_name).cloned().ok_or_else(|| {
86 FlagdEvaluationError::Parse(format!(
87 "Evaluator reference '{}' was not found",
88 ref_name
89 ))
90 })?;
91
92 stack.push(ref_name.to_string());
93 Self::resolve_refs(&mut replacement, evaluators, stack)?;
94 stack.pop();
95
96 *value = replacement;
97 return Ok(());
98 }
99
100 for child in obj.values_mut() {
101 Self::resolve_refs(child, evaluators, stack)?;
102 }
103 Ok(())
104 }
105 Value::Array(items) => {
106 for item in items {
107 Self::resolve_refs(item, evaluators, stack)?;
108 }
109 Ok(())
110 }
111 _ => Ok(()),
112 }
113 }
114
115 fn convert_map_to_hashmap(map: &Map<String, Value>) -> HashMap<String, serde_json::Value> {
116 map.iter().map(|(k, v)| (k.clone(), v.clone())).collect()
117 }
118}
119
120#[cfg(test)]
121#[path = "flag_parser_test.rs"]
122mod tests;