blob: f10ca1ff8b35b515a34105b66fc29853863a1853 [file] [log] [blame]
Mårten Kongstadbb520722023-04-26 13:16:41 +02001/*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17use anyhow::{anyhow, Context, Error, Result};
Mårten Kongstad416330b2023-05-05 11:10:01 +020018use protobuf::{Enum, EnumOrUnknown};
19use serde::{Deserialize, Serialize};
Mårten Kongstadbb520722023-04-26 13:16:41 +020020
Mårten Kongstad09c28d12023-05-04 13:29:26 +020021use crate::protos::{
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +020022 ProtoAndroidConfig, ProtoFlag, ProtoFlagState, ProtoOverride, ProtoOverrideConfig,
23 ProtoPermission, ProtoValue,
Mårten Kongstad09c28d12023-05-04 13:29:26 +020024};
25
Mårten Kongstad416330b2023-05-05 11:10:01 +020026#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +020027pub enum FlagState {
28 Enabled,
29 Disabled,
30}
31
32impl TryFrom<EnumOrUnknown<ProtoFlagState>> for FlagState {
33 type Error = Error;
34
35 fn try_from(proto: EnumOrUnknown<ProtoFlagState>) -> Result<Self, Self::Error> {
36 match ProtoFlagState::from_i32(proto.value()) {
37 Some(ProtoFlagState::ENABLED) => Ok(FlagState::Enabled),
38 Some(ProtoFlagState::DISABLED) => Ok(FlagState::Disabled),
39 None => Err(anyhow!("unknown flag state enum value {}", proto.value())),
40 }
41 }
42}
43
44#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
Mårten Kongstad416330b2023-05-05 11:10:01 +020045pub enum Permission {
46 ReadOnly,
47 ReadWrite,
48}
49
50impl TryFrom<EnumOrUnknown<ProtoPermission>> for Permission {
51 type Error = Error;
52
53 fn try_from(proto: EnumOrUnknown<ProtoPermission>) -> Result<Self, Self::Error> {
54 match ProtoPermission::from_i32(proto.value()) {
55 Some(ProtoPermission::READ_ONLY) => Ok(Permission::ReadOnly),
56 Some(ProtoPermission::READ_WRITE) => Ok(Permission::ReadWrite),
57 None => Err(anyhow!("unknown permission enum value {}", proto.value())),
58 }
59 }
60}
61
Mårten Kongstad09c28d12023-05-04 13:29:26 +020062#[derive(Debug, PartialEq, Eq)]
63pub struct Value {
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +020064 state: FlagState,
Mårten Kongstad416330b2023-05-05 11:10:01 +020065 permission: Permission,
Mårten Kongstad09c28d12023-05-04 13:29:26 +020066 since: Option<u32>,
67}
68
69#[allow(dead_code)] // only used in unit tests
70impl Value {
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +020071 pub fn new(state: FlagState, permission: Permission, since: u32) -> Value {
72 Value { state, permission, since: Some(since) }
Mårten Kongstad09c28d12023-05-04 13:29:26 +020073 }
74
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +020075 pub fn default(state: FlagState, permission: Permission) -> Value {
76 Value { state, permission, since: None }
Mårten Kongstad09c28d12023-05-04 13:29:26 +020077 }
78}
79
80impl TryFrom<ProtoValue> for Value {
81 type Error = Error;
82
83 fn try_from(proto: ProtoValue) -> Result<Self, Self::Error> {
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +020084 let Some(proto_state) = proto.state else {
85 return Err(anyhow!("missing 'state' field"));
Mårten Kongstad09c28d12023-05-04 13:29:26 +020086 };
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +020087 let state = proto_state.try_into()?;
Mårten Kongstad416330b2023-05-05 11:10:01 +020088 let Some(proto_permission) = proto.permission else {
89 return Err(anyhow!("missing 'permission' field"));
90 };
91 let permission = proto_permission.try_into()?;
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +020092 Ok(Value { state, permission, since: proto.since })
Mårten Kongstad09c28d12023-05-04 13:29:26 +020093 }
94}
Mårten Kongstadbb520722023-04-26 13:16:41 +020095
96#[derive(Debug, PartialEq, Eq)]
97pub struct Flag {
98 pub id: String,
99 pub description: String,
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200100
101 // ordered by Value.since; guaranteed to contain at least one item (the default value, with
102 // since == None)
103 pub values: Vec<Value>,
Mårten Kongstadbb520722023-04-26 13:16:41 +0200104}
105
106impl Flag {
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200107 #[allow(dead_code)] // only used in unit tests
Mårten Kongstadbb520722023-04-26 13:16:41 +0200108 pub fn try_from_text_proto(text_proto: &str) -> Result<Flag> {
109 let proto: ProtoFlag = crate::protos::try_from_text_proto(text_proto)
110 .with_context(|| text_proto.to_owned())?;
111 proto.try_into()
112 }
113
114 pub fn try_from_text_proto_list(text_proto: &str) -> Result<Vec<Flag>> {
115 let proto: ProtoAndroidConfig = crate::protos::try_from_text_proto(text_proto)
116 .with_context(|| text_proto.to_owned())?;
117 proto.flag.into_iter().map(|proto_flag| proto_flag.try_into()).collect()
118 }
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200119
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200120 pub fn resolve(&self, build_id: u32) -> (FlagState, Permission) {
121 let mut state = self.values[0].state;
Mårten Kongstad416330b2023-05-05 11:10:01 +0200122 let mut permission = self.values[0].permission;
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200123 for candidate in self.values.iter().skip(1) {
124 let since = candidate.since.expect("invariant: non-defaults values have Some(since)");
125 if since <= build_id {
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200126 state = candidate.state;
Mårten Kongstad416330b2023-05-05 11:10:01 +0200127 permission = candidate.permission;
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200128 }
129 }
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200130 (state, permission)
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200131 }
Mårten Kongstadbb520722023-04-26 13:16:41 +0200132}
133
134impl TryFrom<ProtoFlag> for Flag {
135 type Error = Error;
136
137 fn try_from(proto: ProtoFlag) -> Result<Self, Self::Error> {
138 let Some(id) = proto.id else {
139 return Err(anyhow!("missing 'id' field"));
140 };
141 let Some(description) = proto.description else {
142 return Err(anyhow!("missing 'description' field"));
143 };
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200144 if proto.value.is_empty() {
Mårten Kongstadbb520722023-04-26 13:16:41 +0200145 return Err(anyhow!("missing 'value' field"));
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200146 }
147
148 let mut values: Vec<Value> = vec![];
149 for proto_value in proto.value.into_iter() {
150 let v: Value = proto_value.try_into()?;
151 if values.iter().any(|w| v.since == w.since) {
152 let msg = match v.since {
153 None => format!("flag {}: multiple default values", id),
154 Some(x) => format!("flag {}: multiple values for since={}", id, x),
155 };
156 return Err(anyhow!(msg));
157 }
158 values.push(v);
159 }
160 values.sort_by_key(|v| v.since);
161
162 Ok(Flag { id, description, values })
Mårten Kongstadbb520722023-04-26 13:16:41 +0200163 }
164}
165
166#[derive(Debug, PartialEq, Eq)]
167pub struct Override {
168 pub id: String,
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200169 pub state: FlagState,
Mårten Kongstad416330b2023-05-05 11:10:01 +0200170 pub permission: Permission,
Mårten Kongstadbb520722023-04-26 13:16:41 +0200171}
172
173impl Override {
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200174 #[allow(dead_code)] // only used in unit tests
Mårten Kongstadbb520722023-04-26 13:16:41 +0200175 pub fn try_from_text_proto(text_proto: &str) -> Result<Override> {
176 let proto: ProtoOverride = crate::protos::try_from_text_proto(text_proto)?;
177 proto.try_into()
178 }
179
180 pub fn try_from_text_proto_list(text_proto: &str) -> Result<Vec<Override>> {
181 let proto: ProtoOverrideConfig = crate::protos::try_from_text_proto(text_proto)?;
182 proto.override_.into_iter().map(|proto_flag| proto_flag.try_into()).collect()
183 }
184}
185
186impl TryFrom<ProtoOverride> for Override {
187 type Error = Error;
188
189 fn try_from(proto: ProtoOverride) -> Result<Self, Self::Error> {
190 let Some(id) = proto.id else {
191 return Err(anyhow!("missing 'id' field"));
192 };
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200193 let Some(proto_state) = proto.state else {
194 return Err(anyhow!("missing 'state' field"));
Mårten Kongstadbb520722023-04-26 13:16:41 +0200195 };
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200196 let state = proto_state.try_into()?;
Mårten Kongstad416330b2023-05-05 11:10:01 +0200197 let Some(proto_permission) = proto.permission else {
198 return Err(anyhow!("missing 'permission' field"));
199 };
200 let permission = proto_permission.try_into()?;
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200201 Ok(Override { id, state, permission })
Mårten Kongstadbb520722023-04-26 13:16:41 +0200202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208
209 #[test]
210 fn test_flag_try_from_text_proto() {
211 let expected = Flag {
212 id: "1234".to_owned(),
213 description: "Description of the flag".to_owned(),
Mårten Kongstad416330b2023-05-05 11:10:01 +0200214 values: vec![
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200215 Value::default(FlagState::Disabled, Permission::ReadOnly),
216 Value::new(FlagState::Enabled, Permission::ReadWrite, 8),
Mårten Kongstad416330b2023-05-05 11:10:01 +0200217 ],
Mårten Kongstadbb520722023-04-26 13:16:41 +0200218 };
219
220 let s = r#"
221 id: "1234"
222 description: "Description of the flag"
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200223 value {
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200224 state: DISABLED
Mårten Kongstad416330b2023-05-05 11:10:01 +0200225 permission: READ_ONLY
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200226 }
227 value {
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200228 state: ENABLED
Mårten Kongstad416330b2023-05-05 11:10:01 +0200229 permission: READ_WRITE
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200230 since: 8
231 }
Mårten Kongstadbb520722023-04-26 13:16:41 +0200232 "#;
233 let actual = Flag::try_from_text_proto(s).unwrap();
234
235 assert_eq!(expected, actual);
236 }
237
238 #[test]
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200239 fn test_flag_try_from_text_proto_bad_input() {
240 let s = r#"
241 id: "a"
242 description: "Description of the flag"
243 "#;
244 let error = Flag::try_from_text_proto(s).unwrap_err();
245 assert_eq!(format!("{:?}", error), "missing 'value' field");
246
Mårten Kongstadbb520722023-04-26 13:16:41 +0200247 let s = r#"
248 description: "Description of the flag"
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200249 value {
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200250 state: ENABLED
Mårten Kongstad416330b2023-05-05 11:10:01 +0200251 permission: READ_ONLY
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200252 }
Mårten Kongstadbb520722023-04-26 13:16:41 +0200253 "#;
254 let error = Flag::try_from_text_proto(s).unwrap_err();
255 assert!(format!("{:?}", error).contains("Message not initialized"));
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200256
257 let s = r#"
258 id: "a"
259 description: "Description of the flag"
260 value {
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200261 state: ENABLED
Mårten Kongstad416330b2023-05-05 11:10:01 +0200262 permission: READ_ONLY
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200263 }
264 value {
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200265 state: ENABLED
Mårten Kongstad416330b2023-05-05 11:10:01 +0200266 permission: READ_ONLY
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200267 }
268 "#;
269 let error = Flag::try_from_text_proto(s).unwrap_err();
270 assert_eq!(format!("{:?}", error), "flag a: multiple default values");
Mårten Kongstadbb520722023-04-26 13:16:41 +0200271 }
272
273 #[test]
274 fn test_flag_try_from_text_proto_list() {
275 let expected = vec![
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200276 Flag {
277 id: "a".to_owned(),
278 description: "A".to_owned(),
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200279 values: vec![Value::default(FlagState::Enabled, Permission::ReadOnly)],
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200280 },
281 Flag {
282 id: "b".to_owned(),
283 description: "B".to_owned(),
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200284 values: vec![Value::default(FlagState::Disabled, Permission::ReadWrite)],
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200285 },
Mårten Kongstadbb520722023-04-26 13:16:41 +0200286 ];
287
288 let s = r#"
289 flag {
290 id: "a"
291 description: "A"
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200292 value {
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200293 state: ENABLED
Mårten Kongstad416330b2023-05-05 11:10:01 +0200294 permission: READ_ONLY
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200295 }
Mårten Kongstadbb520722023-04-26 13:16:41 +0200296 }
297 flag {
298 id: "b"
299 description: "B"
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200300 value {
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200301 state: DISABLED
Mårten Kongstad416330b2023-05-05 11:10:01 +0200302 permission: READ_WRITE
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200303 }
Mårten Kongstadbb520722023-04-26 13:16:41 +0200304 }
305 "#;
306 let actual = Flag::try_from_text_proto_list(s).unwrap();
307
308 assert_eq!(expected, actual);
309 }
310
311 #[test]
312 fn test_override_try_from_text_proto_list() {
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200313 let expected = Override {
314 id: "1234".to_owned(),
315 state: FlagState::Enabled,
316 permission: Permission::ReadOnly,
317 };
Mårten Kongstadbb520722023-04-26 13:16:41 +0200318
319 let s = r#"
320 id: "1234"
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200321 state: ENABLED
Mårten Kongstad416330b2023-05-05 11:10:01 +0200322 permission: READ_ONLY
Mårten Kongstadbb520722023-04-26 13:16:41 +0200323 "#;
324 let actual = Override::try_from_text_proto(s).unwrap();
325
326 assert_eq!(expected, actual);
327 }
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200328
329 #[test]
Mårten Kongstad416330b2023-05-05 11:10:01 +0200330 fn test_flag_resolve() {
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200331 let flag = Flag {
332 id: "a".to_owned(),
333 description: "A".to_owned(),
334 values: vec![
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200335 Value::default(FlagState::Disabled, Permission::ReadOnly),
336 Value::new(FlagState::Disabled, Permission::ReadWrite, 10),
337 Value::new(FlagState::Enabled, Permission::ReadOnly, 20),
338 Value::new(FlagState::Enabled, Permission::ReadWrite, 30),
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200339 ],
340 };
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200341 assert_eq!((FlagState::Disabled, Permission::ReadOnly), flag.resolve(0));
342 assert_eq!((FlagState::Disabled, Permission::ReadOnly), flag.resolve(9));
343 assert_eq!((FlagState::Disabled, Permission::ReadWrite), flag.resolve(10));
344 assert_eq!((FlagState::Disabled, Permission::ReadWrite), flag.resolve(11));
345 assert_eq!((FlagState::Disabled, Permission::ReadWrite), flag.resolve(19));
346 assert_eq!((FlagState::Enabled, Permission::ReadOnly), flag.resolve(20));
347 assert_eq!((FlagState::Enabled, Permission::ReadOnly), flag.resolve(21));
348 assert_eq!((FlagState::Enabled, Permission::ReadOnly), flag.resolve(29));
349 assert_eq!((FlagState::Enabled, Permission::ReadWrite), flag.resolve(30));
350 assert_eq!((FlagState::Enabled, Permission::ReadWrite), flag.resolve(10_000));
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200351 }
Mårten Kongstadbb520722023-04-26 13:16:41 +0200352}