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}
21
22/// Represents a multivariate value for a feature flag.
23#[derive(Clone, Debug, Serialize, Deserialize)]
24pub struct FeatureValue {
25    /// The value of the feature.
26    pub value: FlagsmithValue,
27    /// The weight of the feature value variant, as a percentage number (i.e. 100.0).
28    pub weight: f64,
29    /// Priority of the feature flag variant. Lower values indicate a higher priority when multiple variants apply to the same context key.
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub priority: Option<f64>,
32}
33
34/// Represents a feature context for feature flag evaluation.
35#[derive(Clone, Debug, Serialize, Deserialize)]
36pub struct FeatureContext {
37    /// String key used for hashing in percentage splits.
38    pub key: String,
39    /// The name of the feature.
40    pub name: String,
41    /// Whether the feature is enabled.
42    pub enabled: bool,
43    /// The default value for the feature.
44    pub value: FlagsmithValue,
45    /// Priority for this feature context. Lower values indicate higher priority.
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub priority: Option<f64>,
48    /// Multivariate feature variants.
49    #[serde(default, skip_serializing_if = "Vec::is_empty")]
50    pub variants: Vec<FeatureValue>,
51    /// Metadata about the feature.
52    #[serde(default)]
53    pub metadata: FeatureMetadata,
54}
55
56/// Represents environment metadata.
57#[derive(Clone, Debug, Serialize, Deserialize)]
58pub struct EnvironmentContext {
59    /// The environment API key.
60    pub key: String,
61    /// The environment name.
62    pub name: String,
63}
64
65/// Represents identity context for feature flag evaluation.
66#[derive(Clone, Debug, Serialize, Deserialize)]
67pub struct IdentityContext {
68    /// The identity identifier.
69    pub identifier: String,
70    /// String key used for hashing in percentage splits.
71    /// If not provided during deserialization, it will be constructed as "environment_key_identifier".
72    #[serde(default)]
73    pub key: String,
74    /// Identity traits as a map of trait keys to values.
75    #[serde(default)]
76    pub traits: HashMap<String, FlagsmithValue>,
77}
78
79/// Segment rule condition operators.
80#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
81#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
82pub enum ConditionOperator {
83    Equal,
84    NotEqual,
85    GreaterThan,
86    GreaterThanInclusive,
87    LessThan,
88    LessThanInclusive,
89    Contains,
90    NotContains,
91    In,
92    Regex,
93    PercentageSplit,
94    Modulo,
95    IsSet,
96    IsNotSet,
97}
98
99/// Represents a condition value that can be either a single string or an array of strings.
100#[derive(Clone, Debug, Serialize)]
101#[serde(untagged)]
102pub enum ConditionValue {
103    /// Multiple values as an array
104    Multiple(Vec<String>),
105    /// Single value as a string
106    Single(String),
107}
108
109impl<'de> serde::Deserialize<'de> for ConditionValue {
110    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
111    where
112        D: serde::Deserializer<'de>,
113    {
114        use serde_json::Value;
115        let value: Value = serde::Deserialize::deserialize(deserializer)?;
116
117        match value {
118            // If it's already an array, use Multiple
119            Value::Array(arr) => {
120                let strings: Vec<String> = arr
121                    .into_iter()
122                    .map(|v| match v {
123                        Value::String(s) => s,
124                        _ => v.to_string(),
125                    })
126                    .collect();
127                Ok(ConditionValue::Multiple(strings))
128            }
129            // If it's a string, check if it's a JSON array string
130            Value::String(s) => {
131                if s.trim().starts_with('[') {
132                    // Try to parse as JSON array
133                    if let Ok(arr) = serde_json::from_str::<Vec<String>>(&s) {
134                        return Ok(ConditionValue::Multiple(arr));
135                    }
136                }
137                // Otherwise treat as single string
138                Ok(ConditionValue::Single(s))
139            }
140            // For other types, convert to string
141            _ => Ok(ConditionValue::Single(value.to_string())),
142        }
143    }
144}
145
146impl ConditionValue {
147    /// Get the value as a single string (joins arrays with comma)
148    pub fn as_string(&self) -> String {
149        match self {
150            ConditionValue::Single(s) => s.clone(),
151            ConditionValue::Multiple(arr) => arr.join(","),
152        }
153    }
154
155    /// Get values as a Vec (splits single strings by comma, or returns array as-is)
156    pub fn as_vec(&self) -> Vec<String> {
157        match self {
158            ConditionValue::Single(s) => s.split(',').map(|s| s.trim().to_string()).collect(),
159            ConditionValue::Multiple(arr) => arr.clone(),
160        }
161    }
162
163    /// Check if value contains a string (for string-based IN operator)
164    pub fn contains_string(&self, search: &str) -> bool {
165        match self {
166            ConditionValue::Single(s) => s.split(',').any(|v| v.trim() == search),
167            ConditionValue::Multiple(arr) => arr.iter().any(|v| v == search),
168        }
169    }
170}
171
172/// Represents a condition for segment rule evaluation.
173#[derive(Clone, Debug, Serialize, Deserialize)]
174pub struct Condition {
175    /// The operator for this condition.
176    pub operator: ConditionOperator,
177    /// The property to evaluate (can be a JSONPath expression starting with $.).
178    pub property: String,
179    /// The value to compare against (can be a string or array of strings).
180    pub value: ConditionValue,
181}
182
183/// Segment rule types.
184#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
185#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
186pub enum SegmentRuleType {
187    All,
188    Any,
189    None,
190}
191
192/// Represents a segment rule (can be recursive).
193#[derive(Clone, Debug, Serialize, Deserialize)]
194pub struct SegmentRule {
195    /// The type of rule (ALL, ANY, NONE).
196    #[serde(rename = "type")]
197    pub rule_type: SegmentRuleType,
198    /// Conditions for this rule.
199    #[serde(default)]
200    pub conditions: Vec<Condition>,
201    /// Nested rules.
202    #[serde(default)]
203    pub rules: Vec<SegmentRule>,
204}
205
206/// Segment metadata.
207#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
208pub struct SegmentMetadata {
209    /// Segment ID.
210    #[serde(skip_serializing_if = "Option::is_none")]
211    pub segment_id: Option<i32>,
212    /// Source of the segment.
213    pub source: SegmentSource,
214}
215
216impl Default for SegmentMetadata {
217    fn default() -> Self {
218        Self {
219            segment_id: None,
220            source: SegmentSource::Api,
221        }
222    }
223}
224
225/// Represents a segment context for feature flag evaluation.
226#[derive(Clone, Debug, Serialize, Deserialize)]
227pub struct SegmentContext {
228    /// Key used for percentage split segmentation.
229    pub key: String,
230    /// The name of the segment.
231    pub name: String,
232    /// Metadata about the segment.
233    #[serde(default)]
234    pub metadata: SegmentMetadata,
235    /// Feature overrides for the segment.
236    #[serde(default)]
237    pub overrides: Vec<FeatureContext>,
238    /// Rules that define the segment.
239    pub rules: Vec<SegmentRule>,
240}
241
242/// Engine evaluation context that holds pre-processed environment data
243/// for efficient feature flag evaluation.
244#[derive(Clone, Debug, Serialize, Deserialize)]
245pub struct EngineEvaluationContext {
246    /// Environment metadata.
247    pub environment: EnvironmentContext,
248
249    /// Feature contexts indexed by feature name.
250    #[serde(default)]
251    pub features: HashMap<String, FeatureContext>,
252
253    /// Segment contexts indexed by segment key.
254    #[serde(default)]
255    pub segments: HashMap<String, SegmentContext>,
256
257    /// Optional identity context for evaluation.
258    #[serde(skip_serializing_if = "Option::is_none")]
259    pub identity: Option<IdentityContext>,
260}