1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
use std::collections::HashMap;

use crate::EvaluationError;

/// The result of evaluation.
pub type EvaluationResult<T> = Result<T, EvaluationError>;

// ============================================================
//  EvaluationDetails
// ============================================================

/// The result of the flag evaluation process, and made available in the detailed flag resolution
/// functions.
#[derive(Clone, Default, Debug)]
pub struct EvaluationDetails<T> {
    /// The flag key argument passed to the detailed flag evaluation method.
    pub flag_key: String,

    /// The value of evaluation result.
    pub value: T,

    /// The optional returned by the configured provider.
    pub reason: Option<EvaluationReason>,

    /// The optional variant returned by the configured provider.
    pub variant: Option<String>,

    /// The optional flag metadata returned by the configured provider.
    /// If the provider returns nothing, it is set to the default value.
    pub flag_metadata: FlagMetadata,
}

// ============================================================
//  EvaluationReason
// ============================================================

/// Reason for evaluation.
#[derive(Clone, Default, Eq, PartialEq, Debug)]
pub enum EvaluationReason {
    /// The resolved value is static (no dynamic evaluation).
    Static,

    /// The resolved value fell back to a pre-configured value (no dynamic evaluation occurred or
    /// dynamic evaluation yielded no result).
    Default,

    /// The resolved value was the result of a dynamic evaluation, such as a rule or specific
    /// user-targeting.
    TargetingMatch,

    /// The resolved value was the result of pseudorandom assignment.
    Split,

    /// The resolved value was retrieved from cache.
    Cached,

    /// The resolved value was the result of the flag being disabled in the management system.
    Disabled,

    /// The reason for the resolved value could not be determined.
    #[default]
    Unknown,

    /// The resolved value was the result of an error.
    Error,

    /// Other custom reason.
    Other(String),
}

impl ToString for EvaluationReason {
    fn to_string(&self) -> String {
        match self {
            Self::Static => "STATIC",
            Self::Default => "DEFAULT",
            Self::TargetingMatch => "TARGETING_MATCH",
            Self::Split => "SPLIT",
            Self::Cached => "CACHED",
            Self::Disabled => "DISABLED",
            Self::Unknown => "UNKNOWN",
            Self::Error => "ERROR",
            Self::Other(reason) => reason.as_str(),
        }
        .to_string()
    }
}

// ============================================================
//  FlagMetadata
// ============================================================

/// A structure which supports definition of arbitrary properties, with keys of type string, and
/// values of type boolean, string, or number.
///
/// This structure is populated by a provider for use by an Application Author (via the Evaluation
/// API) or an Application Integrator (via hooks).
#[derive(Clone, Default, PartialEq, Debug)]
pub struct FlagMetadata {
    /// The fields of the metadata.
    pub values: HashMap<String, FlagMetadataValue>,
}

impl FlagMetadata {
    /// Append givne `key` and `value` to the fields of metadata.
    #[must_use]
    pub fn with_value(
        mut self,
        key: impl Into<String>,
        value: impl Into<FlagMetadataValue>,
    ) -> Self {
        self.add_value(key, value);
        self
    }

    /// Append givne `key` and `value` to the fields of metadata.
    pub fn add_value(&mut self, key: impl Into<String>, value: impl Into<FlagMetadataValue>) {
        self.values.insert(key.into(), value.into());
    }
}

// ============================================================
//  FlagMetadataValue
// ============================================================

/// Supported values of flag metadata fields.
#[derive(Clone, PartialEq, Debug)]
#[allow(missing_docs)]
pub enum FlagMetadataValue {
    Bool(bool),
    Int(i64),
    Float(f64),
    String(String),
}

impl From<bool> for FlagMetadataValue {
    fn from(value: bool) -> Self {
        Self::Bool(value)
    }
}

impl From<i8> for FlagMetadataValue {
    fn from(value: i8) -> Self {
        Self::Int(value.into())
    }
}

impl From<i16> for FlagMetadataValue {
    fn from(value: i16) -> Self {
        Self::Int(value.into())
    }
}

impl From<i32> for FlagMetadataValue {
    fn from(value: i32) -> Self {
        Self::Int(value.into())
    }
}

impl From<i64> for FlagMetadataValue {
    fn from(value: i64) -> Self {
        Self::Int(value)
    }
}

impl From<u8> for FlagMetadataValue {
    fn from(value: u8) -> Self {
        Self::Int(value.into())
    }
}

impl From<u16> for FlagMetadataValue {
    fn from(value: u16) -> Self {
        Self::Int(value.into())
    }
}

impl From<u32> for FlagMetadataValue {
    fn from(value: u32) -> Self {
        Self::Int(value.into())
    }
}

impl From<f32> for FlagMetadataValue {
    fn from(value: f32) -> Self {
        Self::Float(value.into())
    }
}

impl From<f64> for FlagMetadataValue {
    fn from(value: f64) -> Self {
        Self::Float(value)
    }
}

impl From<String> for FlagMetadataValue {
    fn from(value: String) -> Self {
        Self::String(value)
    }
}

impl From<&str> for FlagMetadataValue {
    fn from(value: &str) -> Self {
        Self::String(value.into())
    }
}