blob: dc185f0d58e002fe4629dfcff21e84cb84743496 [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 Kongstad416330b2023-05-05 11:10:01 +020022 ProtoAndroidConfig, ProtoFlag, ProtoOverride, ProtoOverrideConfig, ProtoPermission, ProtoValue,
Mårten Kongstad09c28d12023-05-04 13:29:26 +020023};
24
Mårten Kongstad416330b2023-05-05 11:10:01 +020025#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
26pub enum Permission {
27 ReadOnly,
28 ReadWrite,
29}
30
31impl TryFrom<EnumOrUnknown<ProtoPermission>> for Permission {
32 type Error = Error;
33
34 fn try_from(proto: EnumOrUnknown<ProtoPermission>) -> Result<Self, Self::Error> {
35 match ProtoPermission::from_i32(proto.value()) {
36 Some(ProtoPermission::READ_ONLY) => Ok(Permission::ReadOnly),
37 Some(ProtoPermission::READ_WRITE) => Ok(Permission::ReadWrite),
38 None => Err(anyhow!("unknown permission enum value {}", proto.value())),
39 }
40 }
41}
42
Mårten Kongstad09c28d12023-05-04 13:29:26 +020043#[derive(Debug, PartialEq, Eq)]
44pub struct Value {
45 value: bool,
Mårten Kongstad416330b2023-05-05 11:10:01 +020046 permission: Permission,
Mårten Kongstad09c28d12023-05-04 13:29:26 +020047 since: Option<u32>,
48}
49
50#[allow(dead_code)] // only used in unit tests
51impl Value {
Mårten Kongstad416330b2023-05-05 11:10:01 +020052 pub fn new(value: bool, permission: Permission, since: u32) -> Value {
53 Value { value, permission, since: Some(since) }
Mårten Kongstad09c28d12023-05-04 13:29:26 +020054 }
55
Mårten Kongstad416330b2023-05-05 11:10:01 +020056 pub fn default(value: bool, permission: Permission) -> Value {
57 Value { value, permission, since: None }
Mårten Kongstad09c28d12023-05-04 13:29:26 +020058 }
59}
60
61impl TryFrom<ProtoValue> for Value {
62 type Error = Error;
63
64 fn try_from(proto: ProtoValue) -> Result<Self, Self::Error> {
65 let Some(value) = proto.value else {
66 return Err(anyhow!("missing 'value' field"));
67 };
Mårten Kongstad416330b2023-05-05 11:10:01 +020068 let Some(proto_permission) = proto.permission else {
69 return Err(anyhow!("missing 'permission' field"));
70 };
71 let permission = proto_permission.try_into()?;
72 Ok(Value { value, permission, since: proto.since })
Mårten Kongstad09c28d12023-05-04 13:29:26 +020073 }
74}
Mårten Kongstadbb520722023-04-26 13:16:41 +020075
76#[derive(Debug, PartialEq, Eq)]
77pub struct Flag {
78 pub id: String,
79 pub description: String,
Mårten Kongstad09c28d12023-05-04 13:29:26 +020080
81 // ordered by Value.since; guaranteed to contain at least one item (the default value, with
82 // since == None)
83 pub values: Vec<Value>,
Mårten Kongstadbb520722023-04-26 13:16:41 +020084}
85
86impl Flag {
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020087 #[allow(dead_code)] // only used in unit tests
Mårten Kongstadbb520722023-04-26 13:16:41 +020088 pub fn try_from_text_proto(text_proto: &str) -> Result<Flag> {
89 let proto: ProtoFlag = crate::protos::try_from_text_proto(text_proto)
90 .with_context(|| text_proto.to_owned())?;
91 proto.try_into()
92 }
93
94 pub fn try_from_text_proto_list(text_proto: &str) -> Result<Vec<Flag>> {
95 let proto: ProtoAndroidConfig = crate::protos::try_from_text_proto(text_proto)
96 .with_context(|| text_proto.to_owned())?;
97 proto.flag.into_iter().map(|proto_flag| proto_flag.try_into()).collect()
98 }
Mårten Kongstad09c28d12023-05-04 13:29:26 +020099
Mårten Kongstad416330b2023-05-05 11:10:01 +0200100 pub fn resolve(&self, build_id: u32) -> (bool, Permission) {
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200101 let mut value = self.values[0].value;
Mårten Kongstad416330b2023-05-05 11:10:01 +0200102 let mut permission = self.values[0].permission;
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200103 for candidate in self.values.iter().skip(1) {
104 let since = candidate.since.expect("invariant: non-defaults values have Some(since)");
105 if since <= build_id {
106 value = candidate.value;
Mårten Kongstad416330b2023-05-05 11:10:01 +0200107 permission = candidate.permission;
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200108 }
109 }
Mårten Kongstad416330b2023-05-05 11:10:01 +0200110 (value, permission)
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200111 }
Mårten Kongstadbb520722023-04-26 13:16:41 +0200112}
113
114impl TryFrom<ProtoFlag> for Flag {
115 type Error = Error;
116
117 fn try_from(proto: ProtoFlag) -> Result<Self, Self::Error> {
118 let Some(id) = proto.id else {
119 return Err(anyhow!("missing 'id' field"));
120 };
121 let Some(description) = proto.description else {
122 return Err(anyhow!("missing 'description' field"));
123 };
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200124 if proto.value.is_empty() {
Mårten Kongstadbb520722023-04-26 13:16:41 +0200125 return Err(anyhow!("missing 'value' field"));
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200126 }
127
128 let mut values: Vec<Value> = vec![];
129 for proto_value in proto.value.into_iter() {
130 let v: Value = proto_value.try_into()?;
131 if values.iter().any(|w| v.since == w.since) {
132 let msg = match v.since {
133 None => format!("flag {}: multiple default values", id),
134 Some(x) => format!("flag {}: multiple values for since={}", id, x),
135 };
136 return Err(anyhow!(msg));
137 }
138 values.push(v);
139 }
140 values.sort_by_key(|v| v.since);
141
142 Ok(Flag { id, description, values })
Mårten Kongstadbb520722023-04-26 13:16:41 +0200143 }
144}
145
146#[derive(Debug, PartialEq, Eq)]
147pub struct Override {
148 pub id: String,
149 pub value: bool,
Mårten Kongstad416330b2023-05-05 11:10:01 +0200150 pub permission: Permission,
Mårten Kongstadbb520722023-04-26 13:16:41 +0200151}
152
153impl Override {
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200154 #[allow(dead_code)] // only used in unit tests
Mårten Kongstadbb520722023-04-26 13:16:41 +0200155 pub fn try_from_text_proto(text_proto: &str) -> Result<Override> {
156 let proto: ProtoOverride = crate::protos::try_from_text_proto(text_proto)?;
157 proto.try_into()
158 }
159
160 pub fn try_from_text_proto_list(text_proto: &str) -> Result<Vec<Override>> {
161 let proto: ProtoOverrideConfig = crate::protos::try_from_text_proto(text_proto)?;
162 proto.override_.into_iter().map(|proto_flag| proto_flag.try_into()).collect()
163 }
164}
165
166impl TryFrom<ProtoOverride> for Override {
167 type Error = Error;
168
169 fn try_from(proto: ProtoOverride) -> Result<Self, Self::Error> {
170 let Some(id) = proto.id else {
171 return Err(anyhow!("missing 'id' field"));
172 };
173 let Some(value) = proto.value else {
174 return Err(anyhow!("missing 'value' field"));
175 };
Mårten Kongstad416330b2023-05-05 11:10:01 +0200176 let Some(proto_permission) = proto.permission else {
177 return Err(anyhow!("missing 'permission' field"));
178 };
179 let permission = proto_permission.try_into()?;
180 Ok(Override { id, value, permission })
Mårten Kongstadbb520722023-04-26 13:16:41 +0200181 }
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187
188 #[test]
189 fn test_flag_try_from_text_proto() {
190 let expected = Flag {
191 id: "1234".to_owned(),
192 description: "Description of the flag".to_owned(),
Mårten Kongstad416330b2023-05-05 11:10:01 +0200193 values: vec![
194 Value::default(false, Permission::ReadOnly),
195 Value::new(true, Permission::ReadWrite, 8),
196 ],
Mårten Kongstadbb520722023-04-26 13:16:41 +0200197 };
198
199 let s = r#"
200 id: "1234"
201 description: "Description of the flag"
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200202 value {
203 value: false
Mårten Kongstad416330b2023-05-05 11:10:01 +0200204 permission: READ_ONLY
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200205 }
206 value {
207 value: true
Mårten Kongstad416330b2023-05-05 11:10:01 +0200208 permission: READ_WRITE
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200209 since: 8
210 }
Mårten Kongstadbb520722023-04-26 13:16:41 +0200211 "#;
212 let actual = Flag::try_from_text_proto(s).unwrap();
213
214 assert_eq!(expected, actual);
215 }
216
217 #[test]
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200218 fn test_flag_try_from_text_proto_bad_input() {
219 let s = r#"
220 id: "a"
221 description: "Description of the flag"
222 "#;
223 let error = Flag::try_from_text_proto(s).unwrap_err();
224 assert_eq!(format!("{:?}", error), "missing 'value' field");
225
Mårten Kongstadbb520722023-04-26 13:16:41 +0200226 let s = r#"
227 description: "Description of the flag"
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200228 value {
229 value: true
Mårten Kongstad416330b2023-05-05 11:10:01 +0200230 permission: READ_ONLY
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200231 }
Mårten Kongstadbb520722023-04-26 13:16:41 +0200232 "#;
233 let error = Flag::try_from_text_proto(s).unwrap_err();
234 assert!(format!("{:?}", error).contains("Message not initialized"));
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200235
236 let s = r#"
237 id: "a"
238 description: "Description of the flag"
239 value {
240 value: true
Mårten Kongstad416330b2023-05-05 11:10:01 +0200241 permission: READ_ONLY
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200242 }
243 value {
244 value: true
Mårten Kongstad416330b2023-05-05 11:10:01 +0200245 permission: READ_ONLY
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200246 }
247 "#;
248 let error = Flag::try_from_text_proto(s).unwrap_err();
249 assert_eq!(format!("{:?}", error), "flag a: multiple default values");
Mårten Kongstadbb520722023-04-26 13:16:41 +0200250 }
251
252 #[test]
253 fn test_flag_try_from_text_proto_list() {
254 let expected = vec![
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200255 Flag {
256 id: "a".to_owned(),
257 description: "A".to_owned(),
Mårten Kongstad416330b2023-05-05 11:10:01 +0200258 values: vec![Value::default(true, Permission::ReadOnly)],
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200259 },
260 Flag {
261 id: "b".to_owned(),
262 description: "B".to_owned(),
Mårten Kongstad416330b2023-05-05 11:10:01 +0200263 values: vec![Value::default(false, Permission::ReadWrite)],
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200264 },
Mårten Kongstadbb520722023-04-26 13:16:41 +0200265 ];
266
267 let s = r#"
268 flag {
269 id: "a"
270 description: "A"
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200271 value {
272 value: true
Mårten Kongstad416330b2023-05-05 11:10:01 +0200273 permission: READ_ONLY
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200274 }
Mårten Kongstadbb520722023-04-26 13:16:41 +0200275 }
276 flag {
277 id: "b"
278 description: "B"
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200279 value {
280 value: false
Mårten Kongstad416330b2023-05-05 11:10:01 +0200281 permission: READ_WRITE
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200282 }
Mårten Kongstadbb520722023-04-26 13:16:41 +0200283 }
284 "#;
285 let actual = Flag::try_from_text_proto_list(s).unwrap();
286
287 assert_eq!(expected, actual);
288 }
289
290 #[test]
291 fn test_override_try_from_text_proto_list() {
Mårten Kongstad416330b2023-05-05 11:10:01 +0200292 let expected =
293 Override { id: "1234".to_owned(), value: true, permission: Permission::ReadOnly };
Mårten Kongstadbb520722023-04-26 13:16:41 +0200294
295 let s = r#"
296 id: "1234"
297 value: true
Mårten Kongstad416330b2023-05-05 11:10:01 +0200298 permission: READ_ONLY
Mårten Kongstadbb520722023-04-26 13:16:41 +0200299 "#;
300 let actual = Override::try_from_text_proto(s).unwrap();
301
302 assert_eq!(expected, actual);
303 }
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200304
305 #[test]
Mårten Kongstad416330b2023-05-05 11:10:01 +0200306 fn test_flag_resolve() {
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200307 let flag = Flag {
308 id: "a".to_owned(),
309 description: "A".to_owned(),
310 values: vec![
Mårten Kongstad416330b2023-05-05 11:10:01 +0200311 Value::default(false, Permission::ReadOnly),
312 Value::new(false, Permission::ReadWrite, 10),
313 Value::new(true, Permission::ReadOnly, 20),
314 Value::new(true, Permission::ReadWrite, 30),
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200315 ],
316 };
Mårten Kongstad416330b2023-05-05 11:10:01 +0200317 assert_eq!((false, Permission::ReadOnly), flag.resolve(0));
318 assert_eq!((false, Permission::ReadOnly), flag.resolve(9));
319 assert_eq!((false, Permission::ReadWrite), flag.resolve(10));
320 assert_eq!((false, Permission::ReadWrite), flag.resolve(11));
321 assert_eq!((false, Permission::ReadWrite), flag.resolve(19));
322 assert_eq!((true, Permission::ReadOnly), flag.resolve(20));
323 assert_eq!((true, Permission::ReadOnly), flag.resolve(21));
324 assert_eq!((true, Permission::ReadOnly), flag.resolve(29));
325 assert_eq!((true, Permission::ReadWrite), flag.resolve(30));
326 assert_eq!((true, Permission::ReadWrite), flag.resolve(10_000));
Mårten Kongstad09c28d12023-05-04 13:29:26 +0200327 }
Mårten Kongstadbb520722023-04-26 13:16:41 +0200328}