blob: c546f7b84ecf8cee354cb7fffd0b02add68eb5d2 [file] [log] [blame]
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +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
Mårten Kongstade66b89f2023-05-15 10:29:25 +020017use anyhow::{bail, ensure, Result};
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020018use serde::{Deserialize, Serialize};
19use std::io::{Read, Write};
20
Mårten Kongstadfa23d292023-05-11 14:47:02 +020021use crate::aconfig::{FlagDeclaration, FlagState, FlagValue, Permission};
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020022use crate::commands::Source;
23
Mårten Kongstadfa23d292023-05-11 14:47:02 +020024const DEFAULT_FLAG_STATE: FlagState = FlagState::Disabled;
25const DEFAULT_FLAG_PERMISSION: Permission = Permission::ReadWrite;
26
Mårten Kongstad416330b2023-05-05 11:10:01 +020027#[derive(Serialize, Deserialize, Debug)]
Mårten Kongstad30950782023-05-09 13:31:29 +020028pub struct Tracepoint {
Mårten Kongstad76adff22023-05-08 10:57:24 +020029 pub source: Source,
30 pub state: FlagState,
31 pub permission: Permission,
32}
33
34#[derive(Serialize, Deserialize, Debug)]
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020035pub struct Item {
Mårten Kongstad30950782023-05-09 13:31:29 +020036 // TODO: duplicating the Cache.namespace as Item.namespace makes the internal representation
37 // closer to the proto message `parsed_flag`; hopefully this will enable us to replace the Item
38 // struct and use a newtype instead once aconfig has matured. Until then, namespace should
39 // really be a Cow<String>.
40 pub namespace: String,
41 pub name: String,
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020042 pub description: String,
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +020043 pub state: FlagState,
Mårten Kongstad416330b2023-05-05 11:10:01 +020044 pub permission: Permission,
Mårten Kongstad30950782023-05-09 13:31:29 +020045 pub trace: Vec<Tracepoint>,
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020046}
47
Mårten Kongstad416330b2023-05-05 11:10:01 +020048#[derive(Serialize, Deserialize, Debug)]
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020049pub struct Cache {
Mårten Kongstad30950782023-05-09 13:31:29 +020050 namespace: String,
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020051 items: Vec<Item>,
52}
53
54impl Cache {
Mårten Kongstade17ba5f2023-05-16 12:52:43 +020055 pub fn new(namespace: String) -> Result<Cache> {
56 ensure!(!namespace.is_empty(), "empty namespace");
57 Ok(Cache { namespace, items: vec![] })
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020058 }
59
60 pub fn read_from_reader(reader: impl Read) -> Result<Cache> {
61 serde_json::from_reader(reader).map_err(|e| e.into())
62 }
63
64 pub fn write_to_writer(&self, writer: impl Write) -> Result<()> {
65 serde_json::to_writer(writer, self).map_err(|e| e.into())
66 }
67
Mårten Kongstadfa23d292023-05-11 14:47:02 +020068 pub fn add_flag_declaration(
69 &mut self,
70 source: Source,
71 declaration: FlagDeclaration,
72 ) -> Result<()> {
Mårten Kongstade17ba5f2023-05-16 12:52:43 +020073 ensure!(!declaration.name.is_empty(), "empty flag name");
74 ensure!(!declaration.description.is_empty(), "empty flag description");
Mårten Kongstade66b89f2023-05-15 10:29:25 +020075 ensure!(
76 self.items.iter().all(|item| item.name != declaration.name),
77 "failed to declare flag {} from {}: flag already declared",
78 declaration.name,
79 source
80 );
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020081 self.items.push(Item {
Mårten Kongstad30950782023-05-09 13:31:29 +020082 namespace: self.namespace.clone(),
Mårten Kongstadfa23d292023-05-11 14:47:02 +020083 name: declaration.name.clone(),
84 description: declaration.description,
85 state: DEFAULT_FLAG_STATE,
86 permission: DEFAULT_FLAG_PERMISSION,
87 trace: vec![Tracepoint {
88 source,
89 state: DEFAULT_FLAG_STATE,
90 permission: DEFAULT_FLAG_PERMISSION,
91 }],
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020092 });
93 Ok(())
94 }
95
Mårten Kongstadfa23d292023-05-11 14:47:02 +020096 pub fn add_flag_value(&mut self, source: Source, value: FlagValue) -> Result<()> {
Mårten Kongstade17ba5f2023-05-16 12:52:43 +020097 ensure!(!value.namespace.is_empty(), "empty flag namespace");
98 ensure!(!value.name.is_empty(), "empty flag name");
Mårten Kongstadfa23d292023-05-11 14:47:02 +020099 ensure!(
100 value.namespace == self.namespace,
101 "failed to set values for flag {}/{} from {}: expected namespace {}",
102 value.namespace,
103 value.name,
104 source,
105 self.namespace
106 );
107 let Some(existing_item) = self.items.iter_mut().find(|item| item.name == value.name) else {
108 bail!("failed to set values for flag {}/{} from {}: flag not declared", value.namespace, value.name, source);
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200109 };
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200110 existing_item.state = value.state;
111 existing_item.permission = value.permission;
Mårten Kongstad30950782023-05-09 13:31:29 +0200112 existing_item.trace.push(Tracepoint {
Mårten Kongstad76adff22023-05-08 10:57:24 +0200113 source,
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200114 state: value.state,
115 permission: value.permission,
Mårten Kongstad76adff22023-05-08 10:57:24 +0200116 });
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200117 Ok(())
118 }
119
120 pub fn iter(&self) -> impl Iterator<Item = &Item> {
121 self.items.iter()
122 }
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200123
Mårten Kongstada1029092023-05-08 11:51:59 +0200124 pub fn into_iter(self) -> impl Iterator<Item = Item> {
125 self.items.into_iter()
126 }
Mårten Kongstad6b9e3822023-05-16 11:19:58 +0200127
128 pub fn namespace(&self) -> &str {
Mårten Kongstade17ba5f2023-05-16 12:52:43 +0200129 debug_assert!(!self.namespace.is_empty());
Mårten Kongstad6b9e3822023-05-16 11:19:58 +0200130 &self.namespace
131 }
Mårten Kongstada1029092023-05-08 11:51:59 +0200132}
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200133
134#[cfg(test)]
135mod tests {
136 use super::*;
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200137 use crate::aconfig::{FlagState, Permission};
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200138
139 #[test]
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200140 fn test_add_flag_declaration() {
Mårten Kongstade17ba5f2023-05-16 12:52:43 +0200141 let mut cache = Cache::new("ns".to_string()).unwrap();
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200142 cache
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200143 .add_flag_declaration(
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200144 Source::File("first.txt".to_string()),
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200145 FlagDeclaration { name: "foo".to_string(), description: "desc".to_string() },
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200146 )
147 .unwrap();
148 let error = cache
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200149 .add_flag_declaration(
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200150 Source::File("second.txt".to_string()),
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200151 FlagDeclaration { name: "foo".to_string(), description: "desc".to_string() },
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200152 )
153 .unwrap_err();
154 assert_eq!(
155 &format!("{:?}", error),
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200156 "failed to declare flag foo from second.txt: flag already declared"
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200157 );
158 }
159
160 #[test]
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200161 fn test_add_flag_value() {
Mårten Kongstad30950782023-05-09 13:31:29 +0200162 fn check(cache: &Cache, name: &str, expected: (FlagState, Permission)) -> bool {
163 let item = cache.iter().find(|&item| item.name == name).unwrap();
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200164 item.state == expected.0 && item.permission == expected.1
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200165 }
166
Mårten Kongstade17ba5f2023-05-16 12:52:43 +0200167 let mut cache = Cache::new("ns".to_string()).unwrap();
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200168 let error = cache
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200169 .add_flag_value(
Mårten Kongstad416330b2023-05-05 11:10:01 +0200170 Source::Memory,
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200171 FlagValue {
Mårten Kongstad30950782023-05-09 13:31:29 +0200172 namespace: "ns".to_string(),
173 name: "foo".to_string(),
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200174 state: FlagState::Enabled,
175 permission: Permission::ReadOnly,
176 },
Mårten Kongstad416330b2023-05-05 11:10:01 +0200177 )
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200178 .unwrap_err();
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200179 assert_eq!(
180 &format!("{:?}", error),
181 "failed to set values for flag ns/foo from <memory>: flag not declared"
182 );
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200183
184 cache
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200185 .add_flag_declaration(
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200186 Source::File("first.txt".to_string()),
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200187 FlagDeclaration { name: "foo".to_string(), description: "desc".to_string() },
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200188 )
189 .unwrap();
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200190 assert!(check(&cache, "foo", (DEFAULT_FLAG_STATE, DEFAULT_FLAG_PERMISSION)));
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200191
192 cache
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200193 .add_flag_value(
Mårten Kongstad416330b2023-05-05 11:10:01 +0200194 Source::Memory,
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200195 FlagValue {
Mårten Kongstad30950782023-05-09 13:31:29 +0200196 namespace: "ns".to_string(),
197 name: "foo".to_string(),
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200198 state: FlagState::Disabled,
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200199 permission: Permission::ReadOnly,
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200200 },
Mårten Kongstad416330b2023-05-05 11:10:01 +0200201 )
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200202 .unwrap();
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200203 assert!(check(&cache, "foo", (FlagState::Disabled, Permission::ReadOnly)));
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200204
205 cache
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200206 .add_flag_value(
Mårten Kongstad416330b2023-05-05 11:10:01 +0200207 Source::Memory,
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200208 FlagValue {
Mårten Kongstad30950782023-05-09 13:31:29 +0200209 namespace: "ns".to_string(),
210 name: "foo".to_string(),
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200211 state: FlagState::Enabled,
212 permission: Permission::ReadWrite,
213 },
Mårten Kongstad416330b2023-05-05 11:10:01 +0200214 )
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200215 .unwrap();
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200216 assert!(check(&cache, "foo", (FlagState::Enabled, Permission::ReadWrite)));
Mårten Kongstad30950782023-05-09 13:31:29 +0200217
218 // different namespace -> no-op
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200219 let error = cache
220 .add_flag_value(
Mårten Kongstad30950782023-05-09 13:31:29 +0200221 Source::Memory,
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200222 FlagValue {
Mårten Kongstad30950782023-05-09 13:31:29 +0200223 namespace: "some-other-namespace".to_string(),
224 name: "foo".to_string(),
225 state: FlagState::Enabled,
226 permission: Permission::ReadOnly,
227 },
228 )
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200229 .unwrap_err();
230 assert_eq!(&format!("{:?}", error), "failed to set values for flag some-other-namespace/foo from <memory>: expected namespace ns");
Mårten Kongstad30950782023-05-09 13:31:29 +0200231 assert!(check(&cache, "foo", (FlagState::Enabled, Permission::ReadWrite)));
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200232 }
Mårten Kongstade17ba5f2023-05-16 12:52:43 +0200233
234 #[test]
235 fn test_reject_empty_cache_namespace() {
236 Cache::new("".to_string()).unwrap_err();
237 }
238
239 #[test]
240 fn test_reject_empty_flag_declaration_fields() {
241 let mut cache = Cache::new("ns".to_string()).unwrap();
242
243 let error = cache
244 .add_flag_declaration(
245 Source::Memory,
246 FlagDeclaration { name: "".to_string(), description: "Description".to_string() },
247 )
248 .unwrap_err();
249 assert_eq!(&format!("{:?}", error), "empty flag name");
250
251 let error = cache
252 .add_flag_declaration(
253 Source::Memory,
254 FlagDeclaration { name: "foo".to_string(), description: "".to_string() },
255 )
256 .unwrap_err();
257 assert_eq!(&format!("{:?}", error), "empty flag description");
258 }
259
260 #[test]
261 fn test_reject_empty_flag_value_files() {
262 let mut cache = Cache::new("ns".to_string()).unwrap();
263 cache
264 .add_flag_declaration(
265 Source::Memory,
266 FlagDeclaration { name: "foo".to_string(), description: "desc".to_string() },
267 )
268 .unwrap();
269
270 let error = cache
271 .add_flag_value(
272 Source::Memory,
273 FlagValue {
274 namespace: "".to_string(),
275 name: "foo".to_string(),
276 state: FlagState::Enabled,
277 permission: Permission::ReadOnly,
278 },
279 )
280 .unwrap_err();
281 assert_eq!(&format!("{:?}", error), "empty flag namespace");
282
283 let error = cache
284 .add_flag_value(
285 Source::Memory,
286 FlagValue {
287 namespace: "ns".to_string(),
288 name: "".to_string(),
289 state: FlagState::Enabled,
290 permission: Permission::ReadOnly,
291 },
292 )
293 .unwrap_err();
294 assert_eq!(&format!("{:?}", error), "empty flag name");
295 }
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200296}