open_feature/provider/
no_op_provider.rs1use async_trait::async_trait;
2
3use crate::{
4 EvaluationContext, EvaluationError, EvaluationErrorCode, EvaluationResult, StructValue,
5};
6
7use super::{FeatureProvider, ProviderMetadata, ProviderStatus, ResolutionDetails};
8
9#[derive(Debug)]
17pub struct NoOpProvider {
18 metadata: ProviderMetadata,
19}
20
21impl Default for NoOpProvider {
22 fn default() -> Self {
23 Self {
24 metadata: ProviderMetadata::new("No-op Provider"),
25 }
26 }
27}
28
29#[async_trait]
30impl FeatureProvider for NoOpProvider {
31 fn metadata(&self) -> &ProviderMetadata {
32 &self.metadata
33 }
34
35 fn status(&self) -> ProviderStatus {
36 ProviderStatus::NotReady
37 }
38
39 async fn resolve_bool_value(
40 &self,
41 _flag_key: &str,
42 _evaluation_context: &EvaluationContext,
43 ) -> EvaluationResult<ResolutionDetails<bool>> {
44 just_error()
45 }
46
47 async fn resolve_int_value(
48 &self,
49 _flag_key: &str,
50 _evaluation_context: &EvaluationContext,
51 ) -> EvaluationResult<ResolutionDetails<i64>> {
52 just_error()
53 }
54
55 async fn resolve_float_value(
56 &self,
57 _flag_key: &str,
58 _evaluation_context: &EvaluationContext,
59 ) -> EvaluationResult<ResolutionDetails<f64>> {
60 just_error()
61 }
62
63 async fn resolve_string_value(
64 &self,
65 _flag_key: &str,
66 _evaluation_context: &EvaluationContext,
67 ) -> EvaluationResult<ResolutionDetails<String>> {
68 just_error()
69 }
70
71 async fn resolve_struct_value(
72 &self,
73 _flag_key: &str,
74 _evaluation_context: &EvaluationContext,
75 ) -> Result<ResolutionDetails<StructValue>, EvaluationError> {
76 just_error()
77 }
78}
79
80fn just_error<T>() -> EvaluationResult<T> {
81 Err(EvaluationError::builder()
82 .code(EvaluationErrorCode::ProviderNotReady)
83 .message("No-op provider is never ready")
84 .build())
85}
86
87#[cfg(test)]
92mod tests {
93 use spec::spec;
94
95 use super::*;
96 use crate::{provider::ProviderStatus, *};
97
98 #[spec(
99 number = "2.1.1",
100 text = "The provider interface MUST define a metadata member or accessor, containing a name field or accessor of type string, which identifies the provider implementation."
101 )]
102 #[test]
103 fn metadata_name() {
104 let provider = NoOpProvider::default();
105
106 assert_eq!(provider.metadata().name, "No-op Provider");
107 }
108
109 #[spec(
110 number = "2.2.1",
111 text = "The feature provider interface MUST define methods to resolve flag values, with parameters flag key (string, required), default value (boolean | number | string | structure, required) and evaluation context (optional), which returns a resolution details structure."
112 )]
113 #[spec(
114 number = "2.2.2.1",
115 text = "The feature provider interface MUST define methods for typed flag resolution, including boolean, numeric, string, and structure."
116 )]
117 #[spec(
118 number = "2.2.3",
119 text = "In cases of normal execution, the provider MUST populate the resolution details structure's value field with the resolved flag value."
120 )]
121 #[spec(
122 number = "2.2.4",
123 text = "In cases of normal execution, the provider SHOULD populate the resolution details structure's variant field with a string identifier corresponding to the returned flag value."
124 )]
125 #[spec(
126 number = "2.2.5",
127 text = r###"The provider SHOULD populate the resolution details structure's reason field with "STATIC", "DEFAULT", "TARGETING_MATCH", "SPLIT", "CACHED", "DISABLED", "UNKNOWN", "STALE", "ERROR" or some other string indicating the semantic reason for the returned flag value."###
128 )]
129 #[spec(
130 number = "2.2.6",
131 text = "In cases of normal execution, the provider MUST NOT populate the resolution details structure's error code field, or otherwise must populate it with a null or falsy value."
132 )]
133 #[spec(
134 number = "2.2.9",
135 text = "The provider SHOULD populate the resolution details structure's flag metadata field. "
136 )]
137 #[spec(
138 number = "2.2.10",
139 text = "flag metadata MUST be a structure supporting the definition of arbitrary properties, with keys of type string, and values of type boolean | string | number."
140 )]
141 #[tokio::test]
142 async fn resolve_value() {
143 let provider = NoOpProvider::default();
144 let context = EvaluationContext::default();
145
146 assert!(provider.resolve_bool_value("", &context).await.is_err());
147 assert!(provider.resolve_int_value("", &context).await.is_err());
148 assert!(provider.resolve_float_value("", &context).await.is_err());
149 assert!(provider.resolve_string_value("", &context).await.is_err());
150 assert!(provider.resolve_struct_value("", &context).await.is_err());
151 }
152
153 #[spec(
154 number = "2.2.7",
155 text = "In cases of abnormal execution, the provider MUST indicate an error using the idioms of the implementation language, with an associated error code and optional associated error message."
156 )]
157 #[test]
158 fn error_code_message_provided_checked_by_type_system() {}
159
160 #[spec(
161 number = "2.2.8.1",
162 text = "The resolution details structure SHOULD accept a generic argument (or use an equivalent language feature) which indicates the type of the wrapped value field."
163 )]
164 #[test]
165 fn resolution_details_generic_checked_by_type_system() {}
166
167 #[spec(
168 number = "2.4.1",
169 text = "The provider MAY define an initialize function which accepts the global evaluation context as an argument and performs initialization logic relevant to the provider."
170 )]
171 #[tokio::test]
172 async fn initialize() {
173 let mut provider = NoOpProvider::default();
174
175 provider.initialize(&EvaluationContext::default()).await;
176 }
177
178 #[spec(
179 number = "2.4.2",
180 text = "The provider MAY define a status field/accessor which indicates the readiness of the provider, with possible values NOT_READY, READY, or ERROR."
181 )]
182 #[spec(
183 number = "2.4.3",
184 text = "The provider MUST set its status field/accessor to READY if its initialize function terminates normally."
185 )]
186 #[spec(
187 number = "2.4.4",
188 text = "The provider MUST set its status field to ERROR if its initialize function terminates abnormally."
189 )]
190 #[spec(
191 number = "2.4.5",
192 text = "The provider SHOULD indicate an error if flag resolution is attempted before the provider is ready."
193 )]
194 #[tokio::test]
195 async fn status() {
196 let provider = NoOpProvider::default();
197 assert_eq!(provider.status(), ProviderStatus::NotReady);
198 }
199
200 #[spec(
201 number = "2.5.1",
202 text = "The provider MAY define a mechanism to gracefully shutdown and dispose of resources."
203 )]
204 #[test]
205 fn shutdown_covered_by_drop_trait() {}
206}