Merge "Add ability to manually write to binary files." into main
This commit is contained in:
@@ -14,6 +14,7 @@ rust_defaults {
|
||||
"libclap",
|
||||
"libcxx",
|
||||
"libaconfig_storage_protos",
|
||||
"libserde",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -36,7 +37,10 @@ rust_binary_host {
|
||||
name: "aconfig-storage",
|
||||
defaults: ["aconfig_storage_file.defaults"],
|
||||
srcs: ["src/main.rs"],
|
||||
rustlibs: ["libaconfig_storage_file"],
|
||||
rustlibs: [
|
||||
"libaconfig_storage_file",
|
||||
"libserde_json",
|
||||
],
|
||||
}
|
||||
|
||||
rust_test_host {
|
||||
|
@@ -14,6 +14,8 @@ tempfile = "3.9.0"
|
||||
thiserror = "1.0.56"
|
||||
clap = { version = "4.1.8", features = ["derive"] }
|
||||
cxx = "1.0"
|
||||
serde = { version = "1.0.152", features = ["derive"] }
|
||||
serde_json = "1.0.93"
|
||||
|
||||
[[bin]]
|
||||
name = "aconfig-storage"
|
||||
|
@@ -20,10 +20,11 @@
|
||||
use crate::{read_str_from_bytes, read_u32_from_bytes, read_u8_from_bytes};
|
||||
use crate::{AconfigStorageError, StorageFileType};
|
||||
use anyhow::anyhow;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
/// Flag info header struct
|
||||
#[derive(PartialEq)]
|
||||
#[derive(PartialEq, Serialize, Deserialize)]
|
||||
pub struct FlagInfoHeader {
|
||||
pub version: u32,
|
||||
pub container: String,
|
||||
@@ -89,7 +90,7 @@ impl FlagInfoHeader {
|
||||
}
|
||||
|
||||
/// bit field for flag info
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum FlagInfoBit {
|
||||
HasServerOverride = 1 << 0,
|
||||
IsReadWrite = 1 << 1,
|
||||
@@ -97,7 +98,7 @@ pub enum FlagInfoBit {
|
||||
}
|
||||
|
||||
/// Flag info node struct
|
||||
#[derive(PartialEq, Clone)]
|
||||
#[derive(PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub struct FlagInfoNode {
|
||||
pub attributes: u8,
|
||||
}
|
||||
@@ -138,7 +139,7 @@ impl FlagInfoNode {
|
||||
}
|
||||
|
||||
/// Flag info list struct
|
||||
#[derive(PartialEq)]
|
||||
#[derive(PartialEq, Serialize, Deserialize)]
|
||||
pub struct FlagInfoList {
|
||||
pub header: FlagInfoHeader,
|
||||
pub nodes: Vec<FlagInfoNode>,
|
||||
|
@@ -23,10 +23,11 @@ use crate::{
|
||||
};
|
||||
use crate::{AconfigStorageError, StorageFileType, StoredFlagType};
|
||||
use anyhow::anyhow;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
/// Flag table header struct
|
||||
#[derive(PartialEq)]
|
||||
#[derive(PartialEq, Serialize, Deserialize)]
|
||||
pub struct FlagTableHeader {
|
||||
pub version: u32,
|
||||
pub container: String,
|
||||
@@ -95,7 +96,7 @@ impl FlagTableHeader {
|
||||
}
|
||||
|
||||
/// Flag table node struct
|
||||
#[derive(PartialEq, Clone)]
|
||||
#[derive(PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub struct FlagTableNode {
|
||||
pub package_id: u32,
|
||||
pub flag_name: String,
|
||||
@@ -154,7 +155,7 @@ impl FlagTableNode {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
#[derive(PartialEq, Serialize, Deserialize)]
|
||||
pub struct FlagTable {
|
||||
pub header: FlagTableHeader,
|
||||
pub buckets: Vec<Option<u32>>,
|
||||
|
@@ -20,10 +20,11 @@
|
||||
use crate::{read_str_from_bytes, read_u32_from_bytes, read_u8_from_bytes};
|
||||
use crate::{AconfigStorageError, StorageFileType};
|
||||
use anyhow::anyhow;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
/// Flag value header struct
|
||||
#[derive(PartialEq)]
|
||||
#[derive(PartialEq, Serialize, Deserialize)]
|
||||
pub struct FlagValueHeader {
|
||||
pub version: u32,
|
||||
pub container: String,
|
||||
@@ -89,7 +90,7 @@ impl FlagValueHeader {
|
||||
}
|
||||
|
||||
/// Flag value list struct
|
||||
#[derive(PartialEq)]
|
||||
#[derive(PartialEq, Serialize, Deserialize)]
|
||||
pub struct FlagValueList {
|
||||
pub header: FlagValueHeader,
|
||||
pub booleans: Vec<bool>,
|
||||
|
@@ -41,6 +41,7 @@ pub mod sip_hasher13;
|
||||
pub mod test_utils;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp::Ordering;
|
||||
use std::fs::File;
|
||||
use std::hash::Hasher;
|
||||
@@ -107,7 +108,7 @@ impl TryFrom<u8> for StorageFileType {
|
||||
|
||||
/// Flag type enum as stored by storage file
|
||||
/// ONLY APPEND, NEVER REMOVE FOR BACKWARD COMPATIBILITY. THE MAX IS U16.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum StoredFlagType {
|
||||
ReadWriteBoolean = 0,
|
||||
ReadOnlyBoolean = 1,
|
||||
|
@@ -20,9 +20,29 @@ use aconfig_storage_file::{
|
||||
list_flags, list_flags_with_info, read_file_to_bytes, AconfigStorageError, FlagInfoList,
|
||||
FlagTable, FlagValueList, PackageTable, StorageFileType,
|
||||
};
|
||||
|
||||
use clap::{builder::ArgAction, Arg, Command};
|
||||
use serde::Serialize;
|
||||
use serde_json;
|
||||
use std::fmt;
|
||||
use std::fs;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
/**
|
||||
* Usage Examples
|
||||
*
|
||||
* Print file:
|
||||
* $ aconfig-storage print --file=path/to/flag.map --type=flag_map
|
||||
*
|
||||
* List flags:
|
||||
* $ aconfig-storage list --flag-map=path/to/flag.map \
|
||||
* --flag-val=path/to/flag.val --package-map=path/to/package.map
|
||||
*
|
||||
* Write binary file for testing:
|
||||
* $ aconfig-storage print --file=path/to/flag.map --type=flag_map --format=json > flag_map.json
|
||||
* $ vim flag_map.json // Manually make updates
|
||||
* $ aconfig-storage write-bytes --input-file=flag_map.json --output-file=path/to/flag.map --type=flag_map
|
||||
*/
|
||||
fn cli() -> Command {
|
||||
Command::new("aconfig-storage")
|
||||
.subcommand_required(true)
|
||||
@@ -34,7 +54,8 @@ fn cli() -> Command {
|
||||
.long("type")
|
||||
.required(true)
|
||||
.value_parser(|s: &str| StorageFileType::try_from(s)),
|
||||
),
|
||||
)
|
||||
.arg(Arg::new("format").long("format").required(false).action(ArgAction::Set)),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("list")
|
||||
@@ -50,41 +71,75 @@ fn cli() -> Command {
|
||||
Arg::new("flag-info").long("flag-info").required(false).action(ArgAction::Set),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("write-bytes")
|
||||
// Where to write the output bytes. Suggest to use the StorageFileType names (e.g. flag.map).
|
||||
.arg(
|
||||
Arg::new("output-file")
|
||||
.long("output-file")
|
||||
.required(true)
|
||||
.action(ArgAction::Set),
|
||||
)
|
||||
// Input file should be json.
|
||||
.arg(
|
||||
Arg::new("input-file").long("input-file").required(true).action(ArgAction::Set),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("type")
|
||||
.long("type")
|
||||
.required(true)
|
||||
.value_parser(|s: &str| StorageFileType::try_from(s)),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fn print_storage_file(
|
||||
file_path: &str,
|
||||
file_type: &StorageFileType,
|
||||
as_json: bool,
|
||||
) -> Result<(), AconfigStorageError> {
|
||||
let bytes = read_file_to_bytes(file_path)?;
|
||||
match file_type {
|
||||
StorageFileType::PackageMap => {
|
||||
let package_table = PackageTable::from_bytes(&bytes)?;
|
||||
println!("{:?}", package_table);
|
||||
println!("{}", to_print_format(package_table, as_json));
|
||||
}
|
||||
StorageFileType::FlagMap => {
|
||||
let flag_table = FlagTable::from_bytes(&bytes)?;
|
||||
println!("{:?}", flag_table);
|
||||
println!("{}", to_print_format(flag_table, as_json));
|
||||
}
|
||||
StorageFileType::FlagVal => {
|
||||
let flag_value = FlagValueList::from_bytes(&bytes)?;
|
||||
println!("{:?}", flag_value);
|
||||
println!("{}", to_print_format(flag_value, as_json));
|
||||
}
|
||||
StorageFileType::FlagInfo => {
|
||||
let flag_info = FlagInfoList::from_bytes(&bytes)?;
|
||||
println!("{:?}", flag_info);
|
||||
println!("{}", to_print_format(flag_info, as_json));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn to_print_format<T>(file_contents: T, as_json: bool) -> String
|
||||
where
|
||||
T: Serialize + fmt::Debug,
|
||||
{
|
||||
if as_json {
|
||||
serde_json::to_string(&file_contents).unwrap()
|
||||
} else {
|
||||
format!("{:?}", file_contents)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<(), AconfigStorageError> {
|
||||
let matches = cli().get_matches();
|
||||
match matches.subcommand() {
|
||||
Some(("print", sub_matches)) => {
|
||||
let file_path = sub_matches.get_one::<String>("file").unwrap();
|
||||
let file_type = sub_matches.get_one::<StorageFileType>("type").unwrap();
|
||||
print_storage_file(file_path, file_type)?
|
||||
let format = sub_matches.get_one::<String>("format");
|
||||
let as_json: bool = format == Some(&"json".to_string());
|
||||
print_storage_file(file_path, file_type, as_json)?
|
||||
}
|
||||
Some(("list", sub_matches)) => {
|
||||
let package_map = sub_matches.get_one::<String>("package-map").unwrap();
|
||||
@@ -96,10 +151,10 @@ fn main() -> Result<(), AconfigStorageError> {
|
||||
let flags = list_flags_with_info(package_map, flag_map, flag_val, info_file)?;
|
||||
for flag in flags.iter() {
|
||||
println!(
|
||||
"{} {} {} {:?} IsReadWrite: {}, HasServerOverride: {}, HasLocalOverride: {}",
|
||||
flag.package_name, flag.flag_name, flag.flag_value, flag.value_type,
|
||||
flag.is_readwrite, flag.has_server_override, flag.has_local_override,
|
||||
);
|
||||
"{} {} {} {:?} IsReadWrite: {}, HasServerOverride: {}, HasLocalOverride: {}",
|
||||
flag.package_name, flag.flag_name, flag.flag_value, flag.value_type,
|
||||
flag.is_readwrite, flag.has_server_override, flag.has_local_override,
|
||||
);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
@@ -113,6 +168,40 @@ fn main() -> Result<(), AconfigStorageError> {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Converts JSON of the file into raw bytes (as is used on-device).
|
||||
// Intended to generate/easily update these files for testing.
|
||||
Some(("write-bytes", sub_matches)) => {
|
||||
let input_file_path = sub_matches.get_one::<String>("input-file").unwrap();
|
||||
let input_json = fs::read_to_string(input_file_path).unwrap();
|
||||
|
||||
let file_type = sub_matches.get_one::<StorageFileType>("type").unwrap();
|
||||
let output_bytes: Vec<u8>;
|
||||
match file_type {
|
||||
StorageFileType::FlagVal => {
|
||||
let list: FlagValueList = serde_json::from_str(&input_json).unwrap();
|
||||
output_bytes = list.into_bytes();
|
||||
}
|
||||
StorageFileType::FlagInfo => {
|
||||
let list: FlagInfoList = serde_json::from_str(&input_json).unwrap();
|
||||
output_bytes = list.into_bytes();
|
||||
}
|
||||
StorageFileType::FlagMap => {
|
||||
let table: FlagTable = serde_json::from_str(&input_json).unwrap();
|
||||
output_bytes = table.into_bytes();
|
||||
}
|
||||
StorageFileType::PackageMap => {
|
||||
let table: PackageTable = serde_json::from_str(&input_json).unwrap();
|
||||
output_bytes = table.into_bytes();
|
||||
}
|
||||
}
|
||||
|
||||
let output_file_path = sub_matches.get_one::<String>("output-file").unwrap();
|
||||
let file = File::create(output_file_path);
|
||||
if file.is_err() {
|
||||
panic!("can't make file");
|
||||
}
|
||||
let _ = file.unwrap().write_all(&output_bytes);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
Ok(())
|
||||
|
@@ -20,10 +20,11 @@
|
||||
use crate::{get_bucket_index, read_str_from_bytes, read_u32_from_bytes, read_u8_from_bytes};
|
||||
use crate::{AconfigStorageError, StorageFileType};
|
||||
use anyhow::anyhow;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
/// Package table header struct
|
||||
#[derive(PartialEq)]
|
||||
#[derive(PartialEq, Serialize, Deserialize)]
|
||||
pub struct PackageTableHeader {
|
||||
pub version: u32,
|
||||
pub container: String,
|
||||
@@ -92,7 +93,7 @@ impl PackageTableHeader {
|
||||
}
|
||||
|
||||
/// Package table node struct
|
||||
#[derive(PartialEq)]
|
||||
#[derive(PartialEq, Serialize, Deserialize)]
|
||||
pub struct PackageTableNode {
|
||||
pub package_name: String,
|
||||
pub package_id: u32,
|
||||
@@ -151,7 +152,7 @@ impl PackageTableNode {
|
||||
}
|
||||
|
||||
/// Package table struct
|
||||
#[derive(PartialEq)]
|
||||
#[derive(PartialEq, Serialize, Deserialize)]
|
||||
pub struct PackageTable {
|
||||
pub header: PackageTableHeader,
|
||||
pub buckets: Vec<Option<u32>>,
|
||||
|
Reference in New Issue
Block a user