1 /*
2  * Copyright (C) 2024 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 
17 use crate::commands::assign_flag_ids;
18 use crate::storage::FlagPackage;
19 use aconfig_protos::ProtoFlagState;
20 use aconfig_storage_file::{FlagValueHeader, FlagValueList, StorageFileType, FILE_VERSION};
21 use anyhow::{anyhow, Result};
22 
new_header(container: &str, num_flags: u32) -> FlagValueHeader23 fn new_header(container: &str, num_flags: u32) -> FlagValueHeader {
24     FlagValueHeader {
25         version: FILE_VERSION,
26         container: String::from(container),
27         file_type: StorageFileType::FlagVal as u8,
28         file_size: 0,
29         num_flags,
30         boolean_value_offset: 0,
31     }
32 }
33 
create_flag_value(container: &str, packages: &[FlagPackage]) -> Result<FlagValueList>34 pub fn create_flag_value(container: &str, packages: &[FlagPackage]) -> Result<FlagValueList> {
35     // create list
36     let num_flags = packages.iter().map(|pkg| pkg.boolean_flags.len() as u32).sum();
37 
38     let mut list = FlagValueList {
39         header: new_header(container, num_flags),
40         booleans: vec![false; num_flags as usize],
41     };
42 
43     for pkg in packages.iter() {
44         let start_index = pkg.boolean_start_index as usize;
45         let flag_ids = assign_flag_ids(pkg.package_name, pkg.boolean_flags.iter().copied())?;
46         for pf in pkg.boolean_flags.iter() {
47             let fid = flag_ids
48                 .get(pf.name())
49                 .ok_or(anyhow!(format!("missing flag id for {}", pf.name())))?;
50 
51             list.booleans[start_index + (*fid as usize)] = pf.state() == ProtoFlagState::ENABLED;
52         }
53     }
54 
55     // initialize all header fields
56     list.header.boolean_value_offset = list.header.into_bytes().len() as u32;
57     list.header.file_size = list.header.boolean_value_offset + num_flags;
58 
59     Ok(list)
60 }
61 
62 #[cfg(test)]
63 mod tests {
64     use super::*;
65     use crate::storage::{group_flags_by_package, tests::parse_all_test_flags};
66 
create_test_flag_value_list_from_source() -> Result<FlagValueList>67     pub fn create_test_flag_value_list_from_source() -> Result<FlagValueList> {
68         let caches = parse_all_test_flags();
69         let packages = group_flags_by_package(caches.iter());
70         create_flag_value("mockup", &packages)
71     }
72 
73     #[test]
74     // this test point locks down the flag value creation and each field
test_list_contents()75     fn test_list_contents() {
76         let flag_value_list = create_test_flag_value_list_from_source();
77         assert!(flag_value_list.is_ok());
78         let expected_flag_value_list =
79             aconfig_storage_file::test_utils::create_test_flag_value_list();
80         assert_eq!(flag_value_list.unwrap(), expected_flag_value_list);
81     }
82 }
83