flagsmith_flag_engine/engine_eval/
context.rs

1use 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    /// Segment came from the Flagsmith API.
9    Api,
10    /// Segment was created from identity overrides.
11    IdentityOverride,
12}
13
14/// Represents metadata information about a feature.
15#[derive(Clone, Debug, Serialize, Deserialize, Default, PartialEq)]
16pub struct FeatureMetadata {
17    /// The feature ID.
18    #[serde(default)]
19    pub feature_id: u32,
20    /// The feature type (e.g., "STANDARD", "MULTIVARIATE").
21    #[serde(default = "default_feature_type")]
22    pub feature_type: String,
23}
24
25fn default_feature_type() -> String {
26    "STANDARD".to_string()
27}
28
29/// Represents a multivariate value for a feature flag.
30#[derive(Clone, Debug, Serialize, Deserialize)]
31pub struct FeatureValue {
32    /// The value of the feature.
33    pub value: FlagsmithValue,
34    /// The weight of the feature value variant, as a percentage number (i.e. 100.0).
35    pub weight: f64,
36    /// Priority of the feature flag variant. Lower values indicate a higher priority when multiple variants apply to the same context key.
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub priority: Option<f64>,
39}
40
41/// Represents a feature context for feature flag evaluation.
42#[derive(Clone, Debug, Serialize, Deserialize)]
43pub struct FeatureContext {
44    /// String key used for hashing in percentage splits.
45    pub key: String,
46    /// The name of the feature.
47    pub name: String,
48    /// Whether the feature is enabled.
49    pub enabled: bool,
50    /// The default value for the feature.
51    pub value: FlagsmithValue,
52    /// Priority for this feature context. Lower values indicate higher priority.
53    #[serde(skip_serializing_if = "Option::is_none")]
54    pub priority: Option<f64>,
55    /// Multivariate feature variants.
56    #[serde(default, skip_serializing_if = "Vec::is_empty")]
57    pub variants: Vec<FeatureValue>,
58    /// Metadata about the feature.
59    #[serde(default)]
60    pub metadata: FeatureMetadata,
61}
62
63/// Represents environment metadata.
64#[derive(Clone, Debug, Serialize, Deserialize)]
65pub struct EnvironmentContext {
66    /// The environment API key.
67    pub key: String,
68    /// The environment name.
69    pub name: String,
70}
71
72/// Represents identity context for feature flag evaluation.
73#[derive(Clone, Debug, Serialize, Deserialize)]
74pub struct IdentityContext {
75    /// The identity identifier.
76    pub identifier: String,
77    /// String key used for hashing in percentage splits.
78    /// If not provided during deserialization, it will be constructed as "environment_key_identifier".
79    #[serde(default)]
80    pub key: String,
81    /// Identity traits as a map of trait keys to values.
82    #[serde(default)]
83    pub traits: HashMap<String, FlagsmithValue>,
84}
85
86/// Segment rule condition operators.
87#[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/// Represents a condition value that can be either a single string or an array of strings.
107#[derive(Clone, Debug, Serialize)]
108#[serde(untagged)]
109pub enum ConditionValue {
110    /// Multiple values as an array
111    Multiple(Vec<String>),
112    /// Single value as a string
113    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            // If it's already an array, use Multiple
126            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            // If it's a string, check if it's a JSON array string
137            Value::String(s) => {
138                if s.trim().starts_with('[') {
139                    // Try to parse as JSON array
140                    if let Ok(arr) = serde_json::from_str::<Vec<String>>(&s) {
141                        return Ok(ConditionValue::Multiple(arr));
142                    }
143                }
144                // Otherwise treat as single string
145                Ok(ConditionValue::Single(s))
146            }
147            // For other types, convert to string
148            _ => Ok(ConditionValue::Single(value.to_string())),
149        }
150    }
151}
152
153impl ConditionValue {
154    /// Get the value as a single string (joins arrays with comma)
155    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    /// Get values as a Vec (splits single strings by comma, or returns array as-is)
163    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    /// Check if value contains a string (for string-based IN operator)
171    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/// Represents a condition for segment rule evaluation.
180#[derive(Clone, Debug, Serialize, Deserialize)]
181pub struct Condition {
182    /// The operator for this condition.
183    pub operator: ConditionOperator,
184    /// The property to evaluate (can be a JSONPath expression starting with $.).
185    pub property: String,
186    /// The value to compare against (can be a string or array of strings).
187    pub value: ConditionValue,
188}
189
190/// Segment rule types.
191#[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/// Represents a segment rule (can be recursive).
200#[derive(Clone, Debug, Serialize, Deserialize)]
201pub struct SegmentRule {
202    /// The type of rule (ALL, ANY, NONE).
203    #[serde(rename = "type")]
204    pub rule_type: SegmentRuleType,
205    /// Conditions for this rule.
206    #[serde(default)]
207    pub conditions: Vec<Condition>,
208    /// Nested rules.
209    #[serde(default)]
210    pub rules: Vec<SegmentRule>,
211}
212
213/// Segment metadata.
214#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
215pub struct SegmentMetadata {
216    /// Segment ID.
217    #[serde(skip_serializing_if = "Option::is_none")]
218    pub segment_id: Option<i32>,
219    /// Source of the segment.
220    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/// Represents a segment context for feature flag evaluation.
233#[derive(Clone, Debug, Serialize, Deserialize)]
234pub struct SegmentContext {
235    /// Key used for percentage split segmentation.
236    pub key: String,
237    /// The name of the segment.
238    pub name: String,
239    /// Metadata about the segment.
240    #[serde(default)]
241    pub metadata: SegmentMetadata,
242    /// Feature overrides for the segment.
243    #[serde(default)]
244    pub overrides: Vec<FeatureContext>,
245    /// Rules that define the segment.
246    pub rules: Vec<SegmentRule>,
247}
248
249/// Engine evaluation context that holds pre-processed environment data
250/// for efficient feature flag evaluation.
251#[derive(Clone, Debug, Serialize, Deserialize)]
252pub struct EngineEvaluationContext {
253    /// Environment metadata.
254    pub environment: EnvironmentContext,
255
256    /// Feature contexts indexed by feature name.
257    #[serde(default)]
258    pub features: HashMap<String, FeatureContext>,
259
260    /// Segment contexts indexed by segment key.
261    #[serde(default)]
262    pub segments: HashMap<String, SegmentContext>,
263
264    /// Optional identity context for evaluation.
265    #[serde(skip_serializing_if = "Option::is_none")]
266    pub identity: Option<IdentityContext>,
267}