blob: edab36c7811f9788c1e16ae8342943d558cbced5 [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 Kongstada1029092023-05-08 11:51:59 +020021use crate::cache::{Cache, Item, TracePoint};
Mårten Kongstad09c28d12023-05-04 13:29:26 +020022use crate::protos::{
Mårten Kongstada1029092023-05-08 11:51:59 +020023 ProtoAndroidConfig, ProtoDump, ProtoDumpItem, ProtoDumpTracePoint, ProtoFlag, ProtoFlagState,
24 ProtoOverride, ProtoOverrideConfig, ProtoPermission, ProtoValue,
Mårten Kongstad09c28d12023-05-04 13:29:26 +020025};
26
Mårten Kongstad416330b2023-05-05 11:10:01 +020027#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +020028pub enum FlagState {
29 Enabled,
30 Disabled,
31}
32
33impl TryFrom<EnumOrUnknown<ProtoFlagState>> for FlagState {
34 type Error = Error;
35
36 fn try_from(proto: EnumOrUnknown<ProtoFlagState>) -> Result<Self, Self::Error> {
37 match ProtoFlagState::from_i32(proto.value()) {
38 Some(ProtoFlagState::ENABLED) => Ok(FlagState::Enabled),
39 Some(ProtoFlagState::DISABLED) => Ok(FlagState::Disabled),
40 None => Err(anyhow!("unknown flag state enum value {}", proto.value())),
41 }
42 }
43}
44
Mårten Kongstada1029092023-05-08 11:51:59 +020045impl From<FlagState> for ProtoFlagState {
46 fn from(state: FlagState) -> Self {
47 match state {
48 FlagState::Enabled => ProtoFlagState::ENABLED,
49 FlagState::Disabled => ProtoFlagState::DISABLED,
50 }
51 }
52}
53
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +020054#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
Mårten Kongstad416330b2023-05-05 11:10:01 +020055pub enum Permission {
56 ReadOnly,
57 ReadWrite,
58}
59
60impl TryFrom<EnumOrUnknown<ProtoPermission>> for Permission {
61 type Error = Error;
62
63 fn try_from(proto: EnumOrUnknown<ProtoPermission>) -> Result<Self, Self::Error> {
64 match ProtoPermission::from_i32(proto.value()) {
65 Some(ProtoPermission::READ_ONLY) => Ok(Permission::ReadOnly),
66 Some(ProtoPermission::READ_WRITE) => Ok(Permission::ReadWrite),
67 None => Err(anyhow!("unknown permission enum value {}", proto.value())),
68 }
69 }
70}
71
Mårten Kongstada1029092023-05-08 11:51:59 +020072impl From<Permission> for ProtoPermission {
73 fn from(permission: Permission) -> Self {
74 match permission {
75 Permission::ReadOnly => ProtoPermission::READ_ONLY,
76 Permission::ReadWrite => ProtoPermission::READ_WRITE,
77 }
78 }
79}
80
Mårten Kongstad09c28d12023-05-04 13:29:26 +020081#[derive(Debug, PartialEq, Eq)]
82pub struct Value {
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +020083 state: FlagState,
Mårten Kongstad416330b2023-05-05 11:10:01 +020084 permission: Permission,
Mårten Kongstad09c28d12023-05-04 13:29:26 +020085 since: Option<u32>,
86}
87
88#[allow(dead_code)] // only used in unit tests
89impl Value {
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +020090 pub fn new(state: FlagState, permission: Permission, since: u32) -> Value {
91 Value { state, permission, since: Some(since) }
Mårten Kongstad09c28d12023-05-04 13:29:26 +020092 }
93
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +020094 pub fn default(state: FlagState, permission: Permission) -> Value {
95 Value { state, permission, since: None }
Mårten Kongstad09c28d12023-05-04 13:29:26 +020096 }
97}
98
99impl TryFrom<ProtoValue> for Value {
100 type Error = Error;
101
102 fn try_from(proto: ProtoValue) -> Result<Self, Self::Error> {
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200103 let Some(proto_state) = proto.state else {
104 return Err(anyhow!("missing 'state' field"));
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200105 };
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200106 let state = proto_state.try_into()?;
Mårten Kongstad416330b2023-05-05 11:10:01 +0200107 let Some(proto_permission) = proto.permission else {
108 return Err(anyhow!("missing 'permission' field"));
109 };
110 let permission = proto_permission.try_into()?;
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200111 Ok(Value { state, permission, since: proto.since })
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200112 }
113}
Mårten Kongstadbb520722023-04-26 13:16:41 +0200114
115#[derive(Debug, PartialEq, Eq)]
116pub struct Flag {
117 pub id: String,
118 pub description: String,
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200119
120 // ordered by Value.since; guaranteed to contain at least one item (the default value, with
121 // since == None)
122 pub values: Vec<Value>,
Mårten Kongstadbb520722023-04-26 13:16:41 +0200123}
124
125impl Flag {
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200126 #[allow(dead_code)] // only used in unit tests
Mårten Kongstadbb520722023-04-26 13:16:41 +0200127 pub fn try_from_text_proto(text_proto: &str) -> Result<Flag> {
128 let proto: ProtoFlag = crate::protos::try_from_text_proto(text_proto)
129 .with_context(|| text_proto.to_owned())?;
130 proto.try_into()
131 }
132
133 pub fn try_from_text_proto_list(text_proto: &str) -> Result<Vec<Flag>> {
134 let proto: ProtoAndroidConfig = crate::protos::try_from_text_proto(text_proto)
135 .with_context(|| text_proto.to_owned())?;
136 proto.flag.into_iter().map(|proto_flag| proto_flag.try_into()).collect()
137 }
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200138
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200139 pub fn resolve(&self, build_id: u32) -> (FlagState, Permission) {
140 let mut state = self.values[0].state;
Mårten Kongstad416330b2023-05-05 11:10:01 +0200141 let mut permission = self.values[0].permission;
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200142 for candidate in self.values.iter().skip(1) {
143 let since = candidate.since.expect("invariant: non-defaults values have Some(since)");
144 if since <= build_id {
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200145 state = candidate.state;
Mårten Kongstad416330b2023-05-05 11:10:01 +0200146 permission = candidate.permission;
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200147 }
148 }
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200149 (state, permission)
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200150 }
Mårten Kongstadbb520722023-04-26 13:16:41 +0200151}
152
153impl TryFrom<ProtoFlag> for Flag {
154 type Error = Error;
155
156 fn try_from(proto: ProtoFlag) -> Result<Self, Self::Error> {
157 let Some(id) = proto.id else {
158 return Err(anyhow!("missing 'id' field"));
159 };
160 let Some(description) = proto.description else {
161 return Err(anyhow!("missing 'description' field"));
162 };
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200163 if proto.value.is_empty() {
Mårten Kongstadbb520722023-04-26 13:16:41 +0200164 return Err(anyhow!("missing 'value' field"));
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200165 }
166
167 let mut values: Vec<Value> = vec![];
168 for proto_value in proto.value.into_iter() {
169 let v: Value = proto_value.try_into()?;
170 if values.iter().any(|w| v.since == w.since) {
171 let msg = match v.since {
172 None => format!("flag {}: multiple default values", id),
173 Some(x) => format!("flag {}: multiple values for since={}", id, x),
174 };
175 return Err(anyhow!(msg));
176 }
177 values.push(v);
178 }
179 values.sort_by_key(|v| v.since);
180
181 Ok(Flag { id, description, values })
Mårten Kongstadbb520722023-04-26 13:16:41 +0200182 }
183}
184
185#[derive(Debug, PartialEq, Eq)]
186pub struct Override {
187 pub id: String,
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200188 pub state: FlagState,
Mårten Kongstad416330b2023-05-05 11:10:01 +0200189 pub permission: Permission,
Mårten Kongstadbb520722023-04-26 13:16:41 +0200190}
191
192impl Override {
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200193 #[allow(dead_code)] // only used in unit tests
Mårten Kongstadbb520722023-04-26 13:16:41 +0200194 pub fn try_from_text_proto(text_proto: &str) -> Result<Override> {
195 let proto: ProtoOverride = crate::protos::try_from_text_proto(text_proto)?;
196 proto.try_into()
197 }
198
199 pub fn try_from_text_proto_list(text_proto: &str) -> Result<Vec<Override>> {
200 let proto: ProtoOverrideConfig = crate::protos::try_from_text_proto(text_proto)?;
201 proto.override_.into_iter().map(|proto_flag| proto_flag.try_into()).collect()
202 }
203}
204
205impl TryFrom<ProtoOverride> for Override {
206 type Error = Error;
207
208 fn try_from(proto: ProtoOverride) -> Result<Self, Self::Error> {
209 let Some(id) = proto.id else {
210 return Err(anyhow!("missing 'id' field"));
211 };
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200212 let Some(proto_state) = proto.state else {
213 return Err(anyhow!("missing 'state' field"));
Mårten Kongstadbb520722023-04-26 13:16:41 +0200214 };
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200215 let state = proto_state.try_into()?;
Mårten Kongstad416330b2023-05-05 11:10:01 +0200216 let Some(proto_permission) = proto.permission else {
217 return Err(anyhow!("missing 'permission' field"));
218 };
219 let permission = proto_permission.try_into()?;
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200220 Ok(Override { id, state, permission })
Mårten Kongstadbb520722023-04-26 13:16:41 +0200221 }
222}
223
Mårten Kongstada1029092023-05-08 11:51:59 +0200224impl From<Cache> for ProtoDump {
225 fn from(cache: Cache) -> Self {
226 let mut dump = ProtoDump::new();
227 for item in cache.into_iter() {
228 dump.item.push(item.into());
229 }
230 dump
231 }
232}
233
234impl From<Item> for ProtoDumpItem {
235 fn from(item: Item) -> Self {
236 let mut dump_item = crate::protos::ProtoDumpItem::new();
237 dump_item.set_id(item.id.clone());
238 dump_item.set_description(item.description.clone());
239 dump_item.set_state(item.state.into());
240 dump_item.set_permission(item.permission.into());
241 for trace in item.trace.into_iter() {
242 dump_item.trace.push(trace.into());
243 }
244 dump_item
245 }
246}
247
248impl From<TracePoint> for ProtoDumpTracePoint {
249 fn from(tracepoint: TracePoint) -> Self {
250 let mut dump_tracepoint = ProtoDumpTracePoint::new();
251 dump_tracepoint.set_source(format!("{}", tracepoint.source));
252 dump_tracepoint.set_state(tracepoint.state.into());
253 dump_tracepoint.set_permission(tracepoint.permission.into());
254 dump_tracepoint
255 }
256}
257
Mårten Kongstadbb520722023-04-26 13:16:41 +0200258#[cfg(test)]
259mod tests {
260 use super::*;
261
262 #[test]
263 fn test_flag_try_from_text_proto() {
264 let expected = Flag {
265 id: "1234".to_owned(),
266 description: "Description of the flag".to_owned(),
Mårten Kongstad416330b2023-05-05 11:10:01 +0200267 values: vec![
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200268 Value::default(FlagState::Disabled, Permission::ReadOnly),
269 Value::new(FlagState::Enabled, Permission::ReadWrite, 8),
Mårten Kongstad416330b2023-05-05 11:10:01 +0200270 ],
Mårten Kongstadbb520722023-04-26 13:16:41 +0200271 };
272
273 let s = r#"
274 id: "1234"
275 description: "Description of the flag"
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200276 value {
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200277 state: DISABLED
Mårten Kongstad416330b2023-05-05 11:10:01 +0200278 permission: READ_ONLY
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200279 }
280 value {
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200281 state: ENABLED
Mårten Kongstad416330b2023-05-05 11:10:01 +0200282 permission: READ_WRITE
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200283 since: 8
284 }
Mårten Kongstadbb520722023-04-26 13:16:41 +0200285 "#;
286 let actual = Flag::try_from_text_proto(s).unwrap();
287
288 assert_eq!(expected, actual);
289 }
290
291 #[test]
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200292 fn test_flag_try_from_text_proto_bad_input() {
293 let s = r#"
294 id: "a"
295 description: "Description of the flag"
296 "#;
297 let error = Flag::try_from_text_proto(s).unwrap_err();
298 assert_eq!(format!("{:?}", error), "missing 'value' field");
299
Mårten Kongstadbb520722023-04-26 13:16:41 +0200300 let s = r#"
301 description: "Description of the flag"
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200302 value {
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200303 state: ENABLED
Mårten Kongstad416330b2023-05-05 11:10:01 +0200304 permission: READ_ONLY
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200305 }
Mårten Kongstadbb520722023-04-26 13:16:41 +0200306 "#;
307 let error = Flag::try_from_text_proto(s).unwrap_err();
308 assert!(format!("{:?}", error).contains("Message not initialized"));
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200309
310 let s = r#"
311 id: "a"
312 description: "Description of the flag"
313 value {
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200314 state: ENABLED
Mårten Kongstad416330b2023-05-05 11:10:01 +0200315 permission: READ_ONLY
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200316 }
317 value {
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200318 state: ENABLED
Mårten Kongstad416330b2023-05-05 11:10:01 +0200319 permission: READ_ONLY
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200320 }
321 "#;
322 let error = Flag::try_from_text_proto(s).unwrap_err();
323 assert_eq!(format!("{:?}", error), "flag a: multiple default values");
Mårten Kongstadbb520722023-04-26 13:16:41 +0200324 }
325
326 #[test]
327 fn test_flag_try_from_text_proto_list() {
328 let expected = vec![
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200329 Flag {
330 id: "a".to_owned(),
331 description: "A".to_owned(),
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200332 values: vec![Value::default(FlagState::Enabled, Permission::ReadOnly)],
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200333 },
334 Flag {
335 id: "b".to_owned(),
336 description: "B".to_owned(),
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200337 values: vec![Value::default(FlagState::Disabled, Permission::ReadWrite)],
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200338 },
Mårten Kongstadbb520722023-04-26 13:16:41 +0200339 ];
340
341 let s = r#"
342 flag {
343 id: "a"
344 description: "A"
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200345 value {
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200346 state: ENABLED
Mårten Kongstad416330b2023-05-05 11:10:01 +0200347 permission: READ_ONLY
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200348 }
Mårten Kongstadbb520722023-04-26 13:16:41 +0200349 }
350 flag {
351 id: "b"
352 description: "B"
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200353 value {
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200354 state: DISABLED
Mårten Kongstad416330b2023-05-05 11:10:01 +0200355 permission: READ_WRITE
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200356 }
Mårten Kongstadbb520722023-04-26 13:16:41 +0200357 }
358 "#;
359 let actual = Flag::try_from_text_proto_list(s).unwrap();
360
361 assert_eq!(expected, actual);
362 }
363
364 #[test]
365 fn test_override_try_from_text_proto_list() {
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200366 let expected = Override {
367 id: "1234".to_owned(),
368 state: FlagState::Enabled,
369 permission: Permission::ReadOnly,
370 };
Mårten Kongstadbb520722023-04-26 13:16:41 +0200371
372 let s = r#"
373 id: "1234"
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200374 state: ENABLED
Mårten Kongstad416330b2023-05-05 11:10:01 +0200375 permission: READ_ONLY
Mårten Kongstadbb520722023-04-26 13:16:41 +0200376 "#;
377 let actual = Override::try_from_text_proto(s).unwrap();
378
379 assert_eq!(expected, actual);
380 }
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200381
382 #[test]
Mårten Kongstad416330b2023-05-05 11:10:01 +0200383 fn test_flag_resolve() {
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200384 let flag = Flag {
385 id: "a".to_owned(),
386 description: "A".to_owned(),
387 values: vec![
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200388 Value::default(FlagState::Disabled, Permission::ReadOnly),
389 Value::new(FlagState::Disabled, Permission::ReadWrite, 10),
390 Value::new(FlagState::Enabled, Permission::ReadOnly, 20),
391 Value::new(FlagState::Enabled, Permission::ReadWrite, 30),
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200392 ],
393 };
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200394 assert_eq!((FlagState::Disabled, Permission::ReadOnly), flag.resolve(0));
395 assert_eq!((FlagState::Disabled, Permission::ReadOnly), flag.resolve(9));
396 assert_eq!((FlagState::Disabled, Permission::ReadWrite), flag.resolve(10));
397 assert_eq!((FlagState::Disabled, Permission::ReadWrite), flag.resolve(11));
398 assert_eq!((FlagState::Disabled, Permission::ReadWrite), flag.resolve(19));
399 assert_eq!((FlagState::Enabled, Permission::ReadOnly), flag.resolve(20));
400 assert_eq!((FlagState::Enabled, Permission::ReadOnly), flag.resolve(21));
401 assert_eq!((FlagState::Enabled, Permission::ReadOnly), flag.resolve(29));
402 assert_eq!((FlagState::Enabled, Permission::ReadWrite), flag.resolve(30));
403 assert_eq!((FlagState::Enabled, Permission::ReadWrite), flag.resolve(10_000));
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200404 }
Mårten Kongstadbb520722023-04-26 13:16:41 +0200405}