flagsmith_flag_engine/engine_eval/
context.rs1use crate::types::FlagsmithValue;
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4
5#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
6#[serde(rename_all = "snake_case")]
7pub enum SegmentSource {
8 Api,
10 IdentityOverride,
12}
13
14#[derive(Clone, Debug, Serialize, Deserialize, Default, PartialEq)]
16pub struct FeatureMetadata {
17 #[serde(default)]
19 pub feature_id: u32,
20 #[serde(default = "default_feature_type")]
22 pub feature_type: String,
23}
24
25fn default_feature_type() -> String {
26 "STANDARD".to_string()
27}
28
29#[derive(Clone, Debug, Serialize, Deserialize)]
31pub struct FeatureValue {
32 pub value: FlagsmithValue,
34 pub weight: f64,
36 #[serde(skip_serializing_if = "Option::is_none")]
38 pub priority: Option<f64>,
39}
40
41#[derive(Clone, Debug, Serialize, Deserialize)]
43pub struct FeatureContext {
44 pub key: String,
46 pub name: String,
48 pub enabled: bool,
50 pub value: FlagsmithValue,
52 #[serde(skip_serializing_if = "Option::is_none")]
54 pub priority: Option<f64>,
55 #[serde(default, skip_serializing_if = "Vec::is_empty")]
57 pub variants: Vec<FeatureValue>,
58 #[serde(default)]
60 pub metadata: FeatureMetadata,
61}
62
63#[derive(Clone, Debug, Serialize, Deserialize)]
65pub struct EnvironmentContext {
66 pub key: String,
68 pub name: String,
70}
71
72#[derive(Clone, Debug, Serialize, Deserialize)]
74pub struct IdentityContext {
75 pub identifier: String,
77 #[serde(default)]
80 pub key: String,
81 #[serde(default)]
83 pub traits: HashMap<String, FlagsmithValue>,
84}
85
86#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
88#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
89pub enum ConditionOperator {
90 Equal,
91 NotEqual,
92 GreaterThan,
93 GreaterThanInclusive,
94 LessThan,
95 LessThanInclusive,
96 Contains,
97 NotContains,
98 In,
99 Regex,
100 PercentageSplit,
101 Modulo,
102 IsSet,
103 IsNotSet,
104}
105
106#[derive(Clone, Debug, Serialize)]
108#[serde(untagged)]
109pub enum ConditionValue {
110 Multiple(Vec<String>),
112 Single(String),
114}
115
116impl<'de> serde::Deserialize<'de> for ConditionValue {
117 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
118 where
119 D: serde::Deserializer<'de>,
120 {
121 use serde_json::Value;
122 let value: Value = serde::Deserialize::deserialize(deserializer)?;
123
124 match value {
125 Value::Array(arr) => {
127 let strings: Vec<String> = arr
128 .into_iter()
129 .map(|v| match v {
130 Value::String(s) => s,
131 _ => v.to_string(),
132 })
133 .collect();
134 Ok(ConditionValue::Multiple(strings))
135 }
136 Value::String(s) => {
138 if s.trim().starts_with('[') {
139 if let Ok(arr) = serde_json::from_str::<Vec<String>>(&s) {
141 return Ok(ConditionValue::Multiple(arr));
142 }
143 }
144 Ok(ConditionValue::Single(s))
146 }
147 _ => Ok(ConditionValue::Single(value.to_string())),
149 }
150 }
151}
152
153impl ConditionValue {
154 pub fn as_string(&self) -> String {
156 match self {
157 ConditionValue::Single(s) => s.clone(),
158 ConditionValue::Multiple(arr) => arr.join(","),
159 }
160 }
161
162 pub fn as_vec(&self) -> Vec<String> {
164 match self {
165 ConditionValue::Single(s) => s.split(',').map(|s| s.trim().to_string()).collect(),
166 ConditionValue::Multiple(arr) => arr.clone(),
167 }
168 }
169
170 pub fn contains_string(&self, search: &str) -> bool {
172 match self {
173 ConditionValue::Single(s) => s.split(',').any(|v| v.trim() == search),
174 ConditionValue::Multiple(arr) => arr.iter().any(|v| v == search),
175 }
176 }
177}
178
179#[derive(Clone, Debug, Serialize, Deserialize)]
181pub struct Condition {
182 pub operator: ConditionOperator,
184 pub property: String,
186 pub value: ConditionValue,
188}
189
190#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
192#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
193pub enum SegmentRuleType {
194 All,
195 Any,
196 None,
197}
198
199#[derive(Clone, Debug, Serialize, Deserialize)]
201pub struct SegmentRule {
202 #[serde(rename = "type")]
204 pub rule_type: SegmentRuleType,
205 #[serde(default)]
207 pub conditions: Vec<Condition>,
208 #[serde(default)]
210 pub rules: Vec<SegmentRule>,
211}
212
213#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
215pub struct SegmentMetadata {
216 #[serde(skip_serializing_if = "Option::is_none")]
218 pub segment_id: Option<i32>,
219 pub source: SegmentSource,
221}
222
223impl Default for SegmentMetadata {
224 fn default() -> Self {
225 Self {
226 segment_id: None,
227 source: SegmentSource::Api,
228 }
229 }
230}
231
232#[derive(Clone, Debug, Serialize, Deserialize)]
234pub struct SegmentContext {
235 pub key: String,
237 pub name: String,
239 #[serde(default)]
241 pub metadata: SegmentMetadata,
242 #[serde(default)]
244 pub overrides: Vec<FeatureContext>,
245 pub rules: Vec<SegmentRule>,
247}
248
249#[derive(Clone, Debug, Serialize, Deserialize)]
252pub struct EngineEvaluationContext {
253 pub environment: EnvironmentContext,
255
256 #[serde(default)]
258 pub features: HashMap<String, FeatureContext>,
259
260 #[serde(default)]
262 pub segments: HashMap<String, SegmentContext>,
263
264 #[serde(skip_serializing_if = "Option::is_none")]
266 pub identity: Option<IdentityContext>,
267}