blob: 3ecadc92533d37dc3792a89830eabe4eea267c7c [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 Kongstadfa23d292023-05-11 14:47:02 +020017use anyhow::{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 Kongstadfa23d292023-05-11 14:47:02 +020055 pub fn new(namespace: String) -> Cache {
56 Cache { namespace, items: vec![] }
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020057 }
58
59 pub fn read_from_reader(reader: impl Read) -> Result<Cache> {
60 serde_json::from_reader(reader).map_err(|e| e.into())
61 }
62
63 pub fn write_to_writer(&self, writer: impl Write) -> Result<()> {
64 serde_json::to_writer(writer, self).map_err(|e| e.into())
65 }
66
Mårten Kongstadfa23d292023-05-11 14:47:02 +020067 pub fn add_flag_declaration(
68 &mut self,
69 source: Source,
70 declaration: FlagDeclaration,
71 ) -> Result<()> {
72 if self.items.iter().any(|item| item.name == declaration.name) {
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020073 return Err(anyhow!(
Mårten Kongstadfa23d292023-05-11 14:47:02 +020074 "failed to declare flag {} from {}: flag already declared",
75 declaration.name,
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020076 source,
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020077 ));
78 }
79 self.items.push(Item {
Mårten Kongstad30950782023-05-09 13:31:29 +020080 namespace: self.namespace.clone(),
Mårten Kongstadfa23d292023-05-11 14:47:02 +020081 name: declaration.name.clone(),
82 description: declaration.description,
83 state: DEFAULT_FLAG_STATE,
84 permission: DEFAULT_FLAG_PERMISSION,
85 trace: vec![Tracepoint {
86 source,
87 state: DEFAULT_FLAG_STATE,
88 permission: DEFAULT_FLAG_PERMISSION,
89 }],
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020090 });
91 Ok(())
92 }
93
Mårten Kongstadfa23d292023-05-11 14:47:02 +020094 pub fn add_flag_value(&mut self, source: Source, value: FlagValue) -> Result<()> {
95 ensure!(
96 value.namespace == self.namespace,
97 "failed to set values for flag {}/{} from {}: expected namespace {}",
98 value.namespace,
99 value.name,
100 source,
101 self.namespace
102 );
103 let Some(existing_item) = self.items.iter_mut().find(|item| item.name == value.name) else {
104 bail!("failed to set values for flag {}/{} from {}: flag not declared", value.namespace, value.name, source);
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200105 };
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200106 existing_item.state = value.state;
107 existing_item.permission = value.permission;
Mårten Kongstad30950782023-05-09 13:31:29 +0200108 existing_item.trace.push(Tracepoint {
Mårten Kongstad76adff22023-05-08 10:57:24 +0200109 source,
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200110 state: value.state,
111 permission: value.permission,
Mårten Kongstad76adff22023-05-08 10:57:24 +0200112 });
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200113 Ok(())
114 }
115
116 pub fn iter(&self) -> impl Iterator<Item = &Item> {
117 self.items.iter()
118 }
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200119
Mårten Kongstada1029092023-05-08 11:51:59 +0200120 pub fn into_iter(self) -> impl Iterator<Item = Item> {
121 self.items.into_iter()
122 }
123}
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200124
125#[cfg(test)]
126mod tests {
127 use super::*;
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200128 use crate::aconfig::{FlagState, Permission};
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200129
130 #[test]
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200131 fn test_add_flag_declaration() {
132 let mut cache = Cache::new("ns".to_string());
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200133 cache
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200134 .add_flag_declaration(
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200135 Source::File("first.txt".to_string()),
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200136 FlagDeclaration { name: "foo".to_string(), description: "desc".to_string() },
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200137 )
138 .unwrap();
139 let error = cache
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200140 .add_flag_declaration(
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200141 Source::File("second.txt".to_string()),
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200142 FlagDeclaration { name: "foo".to_string(), description: "desc".to_string() },
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200143 )
144 .unwrap_err();
145 assert_eq!(
146 &format!("{:?}", error),
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200147 "failed to declare flag foo from second.txt: flag already declared"
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200148 );
149 }
150
151 #[test]
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200152 fn test_add_flag_value() {
Mårten Kongstad30950782023-05-09 13:31:29 +0200153 fn check(cache: &Cache, name: &str, expected: (FlagState, Permission)) -> bool {
154 let item = cache.iter().find(|&item| item.name == name).unwrap();
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200155 item.state == expected.0 && item.permission == expected.1
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200156 }
157
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200158 let mut cache = Cache::new("ns".to_string());
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200159 let error = cache
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200160 .add_flag_value(
Mårten Kongstad416330b2023-05-05 11:10:01 +0200161 Source::Memory,
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200162 FlagValue {
Mårten Kongstad30950782023-05-09 13:31:29 +0200163 namespace: "ns".to_string(),
164 name: "foo".to_string(),
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200165 state: FlagState::Enabled,
166 permission: Permission::ReadOnly,
167 },
Mårten Kongstad416330b2023-05-05 11:10:01 +0200168 )
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200169 .unwrap_err();
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200170 assert_eq!(
171 &format!("{:?}", error),
172 "failed to set values for flag ns/foo from <memory>: flag not declared"
173 );
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200174
175 cache
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200176 .add_flag_declaration(
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200177 Source::File("first.txt".to_string()),
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200178 FlagDeclaration { name: "foo".to_string(), description: "desc".to_string() },
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200179 )
180 .unwrap();
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200181 assert!(check(&cache, "foo", (DEFAULT_FLAG_STATE, DEFAULT_FLAG_PERMISSION)));
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200182
183 cache
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200184 .add_flag_value(
Mårten Kongstad416330b2023-05-05 11:10:01 +0200185 Source::Memory,
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200186 FlagValue {
Mårten Kongstad30950782023-05-09 13:31:29 +0200187 namespace: "ns".to_string(),
188 name: "foo".to_string(),
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200189 state: FlagState::Disabled,
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200190 permission: Permission::ReadOnly,
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200191 },
Mårten Kongstad416330b2023-05-05 11:10:01 +0200192 )
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200193 .unwrap();
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200194 assert!(check(&cache, "foo", (FlagState::Disabled, Permission::ReadOnly)));
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200195
196 cache
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200197 .add_flag_value(
Mårten Kongstad416330b2023-05-05 11:10:01 +0200198 Source::Memory,
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200199 FlagValue {
Mårten Kongstad30950782023-05-09 13:31:29 +0200200 namespace: "ns".to_string(),
201 name: "foo".to_string(),
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200202 state: FlagState::Enabled,
203 permission: Permission::ReadWrite,
204 },
Mårten Kongstad416330b2023-05-05 11:10:01 +0200205 )
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200206 .unwrap();
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200207 assert!(check(&cache, "foo", (FlagState::Enabled, Permission::ReadWrite)));
Mårten Kongstad30950782023-05-09 13:31:29 +0200208
209 // different namespace -> no-op
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200210 let error = cache
211 .add_flag_value(
Mårten Kongstad30950782023-05-09 13:31:29 +0200212 Source::Memory,
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200213 FlagValue {
Mårten Kongstad30950782023-05-09 13:31:29 +0200214 namespace: "some-other-namespace".to_string(),
215 name: "foo".to_string(),
216 state: FlagState::Enabled,
217 permission: Permission::ReadOnly,
218 },
219 )
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200220 .unwrap_err();
221 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 +0200222 assert!(check(&cache, "foo", (FlagState::Enabled, Permission::ReadWrite)));
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200223 }
224}