blob: d60cc200e1c8a33c8890c77b60de84b3f11bc210 [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
17use anyhow::{anyhow, Result};
18use serde::{Deserialize, Serialize};
19use std::io::{Read, Write};
20
21use crate::aconfig::{Flag, Override};
22use crate::commands::Source;
23
24#[derive(Serialize, Deserialize)]
25pub struct Value {
26 pub value: bool,
27 pub source: Source,
28}
29
30#[derive(Serialize, Deserialize)]
31pub struct Item {
32 pub id: String,
33 pub description: String,
34 pub values: Vec<Value>,
35}
36
37#[derive(Serialize, Deserialize)]
38pub struct Cache {
39 items: Vec<Item>,
40}
41
42impl Cache {
43 pub fn new() -> Cache {
44 Cache { items: vec![] }
45 }
46
47 pub fn read_from_reader(reader: impl Read) -> Result<Cache> {
48 serde_json::from_reader(reader).map_err(|e| e.into())
49 }
50
51 pub fn write_to_writer(&self, writer: impl Write) -> Result<()> {
52 serde_json::to_writer(writer, self).map_err(|e| e.into())
53 }
54
55 pub fn add_flag(&mut self, source: Source, flag: Flag) -> Result<()> {
56 if let Some(existing_item) = self.items.iter().find(|&item| item.id == flag.id) {
57 return Err(anyhow!(
58 "failed to add flag {} from {}: already added from {}",
59 flag.id,
60 source,
61 existing_item.values.first().unwrap().source
62 ));
63 }
64 self.items.push(Item {
65 id: flag.id.clone(),
66 description: flag.description.clone(),
67 values: vec![Value { value: flag.value, source }],
68 });
69 Ok(())
70 }
71
72 pub fn add_override(&mut self, source: Source, override_: Override) -> Result<()> {
73 let Some(existing_item) = self.items.iter_mut().find(|item| item.id == override_.id) else {
74 return Err(anyhow!("failed to override flag {}: unknown flag", override_.id));
75 };
76 existing_item.values.push(Value { value: override_.value, source });
77 Ok(())
78 }
79
80 pub fn iter(&self) -> impl Iterator<Item = &Item> {
81 self.items.iter()
82 }
83}
84
85impl Item {
86 pub fn value(&self) -> bool {
87 self.values.last().unwrap().value
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn test_add_flag() {
97 let mut cache = Cache::new();
98 cache
99 .add_flag(
100 Source::File("first.txt".to_string()),
101 Flag { id: "foo".to_string(), description: "desc".to_string(), value: true },
102 )
103 .unwrap();
104 let error = cache
105 .add_flag(
106 Source::File("second.txt".to_string()),
107 Flag { id: "foo".to_string(), description: "desc".to_string(), value: false },
108 )
109 .unwrap_err();
110 assert_eq!(
111 &format!("{:?}", error),
112 "failed to add flag foo from second.txt: already added from first.txt"
113 );
114 }
115
116 #[test]
117 fn test_add_override() {
118 fn get_value(cache: &Cache, id: &str) -> bool {
119 cache.iter().find(|&item| item.id == id).unwrap().value()
120 }
121
122 let mut cache = Cache::new();
123 let error = cache
124 .add_override(Source::Memory, Override { id: "foo".to_string(), value: false })
125 .unwrap_err();
126 assert_eq!(&format!("{:?}", error), "failed to override flag foo: unknown flag");
127
128 cache
129 .add_flag(
130 Source::File("first.txt".to_string()),
131 Flag { id: "foo".to_string(), description: "desc".to_string(), value: true },
132 )
133 .unwrap();
134 assert!(get_value(&cache, "foo"));
135
136 cache
137 .add_override(Source::Memory, Override { id: "foo".to_string(), value: false })
138 .unwrap();
139 assert!(!get_value(&cache, "foo"));
140
141 cache
142 .add_override(Source::Memory, Override { id: "foo".to_string(), value: true })
143 .unwrap();
144 assert!(get_value(&cache, "foo"));
145 }
146}