aconfig: allow dots in package fields
Allow package fields to include dots. Update the generated code based on the package name: if the package name is com.android.example: - java: package com.android.example; ... - C++: namespace com::android::example { ... } - Rust: mod com { mod android { mod example { ... } } } Also, update examples to use dots in the package fields. Also, remove unnecessary #include from the auto-generated C++ code: the header should not include itself. Bug: 285000854 Test: atest aconfig.test Change-Id: I8a5352e25c64c34dee0725202a1b7c9957819de8
This commit is contained in:
@@ -244,7 +244,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_package_try_from_text_proto() {
|
fn test_package_try_from_text_proto() {
|
||||||
let expected = FlagDeclarations {
|
let expected = FlagDeclarations {
|
||||||
package: "ns".to_owned(),
|
package: "com.example".to_owned(),
|
||||||
flags: vec![
|
flags: vec![
|
||||||
FlagDeclaration { name: "a".to_owned(), description: "A".to_owned() },
|
FlagDeclaration { name: "a".to_owned(), description: "A".to_owned() },
|
||||||
FlagDeclaration { name: "b".to_owned(), description: "B".to_owned() },
|
FlagDeclaration { name: "b".to_owned(), description: "B".to_owned() },
|
||||||
@@ -252,7 +252,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let s = r#"
|
let s = r#"
|
||||||
package: "ns"
|
package: "com.example"
|
||||||
flag {
|
flag {
|
||||||
name: "a"
|
name: "a"
|
||||||
description: "A"
|
description: "A"
|
||||||
@@ -270,14 +270,14 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_flag_declaration_try_from_text_proto_list() {
|
fn test_flag_declaration_try_from_text_proto_list() {
|
||||||
let expected = FlagValue {
|
let expected = FlagValue {
|
||||||
package: "ns".to_owned(),
|
package: "com.example".to_owned(),
|
||||||
name: "1234".to_owned(),
|
name: "1234".to_owned(),
|
||||||
state: FlagState::Enabled,
|
state: FlagState::Enabled,
|
||||||
permission: Permission::ReadOnly,
|
permission: Permission::ReadOnly,
|
||||||
};
|
};
|
||||||
|
|
||||||
let s = r#"
|
let s = r#"
|
||||||
package: "ns"
|
package: "com.example"
|
||||||
name: "1234"
|
name: "1234"
|
||||||
state: ENABLED
|
state: ENABLED
|
||||||
permission: READ_ONLY
|
permission: READ_ONLY
|
||||||
|
@@ -109,7 +109,7 @@ pub struct CacheBuilder {
|
|||||||
|
|
||||||
impl CacheBuilder {
|
impl CacheBuilder {
|
||||||
pub fn new(package: String) -> Result<CacheBuilder> {
|
pub fn new(package: String) -> Result<CacheBuilder> {
|
||||||
ensure!(codegen::is_valid_identifier(&package), "bad package");
|
ensure!(codegen::is_valid_package_ident(&package), "bad package");
|
||||||
let cache = Cache { package, items: vec![] };
|
let cache = Cache { package, items: vec![] };
|
||||||
Ok(CacheBuilder { cache })
|
Ok(CacheBuilder { cache })
|
||||||
}
|
}
|
||||||
@@ -119,7 +119,7 @@ impl CacheBuilder {
|
|||||||
source: Source,
|
source: Source,
|
||||||
declaration: FlagDeclaration,
|
declaration: FlagDeclaration,
|
||||||
) -> Result<&mut CacheBuilder> {
|
) -> Result<&mut CacheBuilder> {
|
||||||
ensure!(codegen::is_valid_identifier(&declaration.name), "bad flag name");
|
ensure!(codegen::is_valid_name_ident(&declaration.name), "bad flag name");
|
||||||
ensure!(!declaration.description.is_empty(), "empty flag description");
|
ensure!(!declaration.description.is_empty(), "empty flag description");
|
||||||
ensure!(
|
ensure!(
|
||||||
self.cache.items.iter().all(|item| item.name != declaration.name),
|
self.cache.items.iter().all(|item| item.name != declaration.name),
|
||||||
@@ -147,8 +147,8 @@ impl CacheBuilder {
|
|||||||
source: Source,
|
source: Source,
|
||||||
value: FlagValue,
|
value: FlagValue,
|
||||||
) -> Result<&mut CacheBuilder> {
|
) -> Result<&mut CacheBuilder> {
|
||||||
ensure!(codegen::is_valid_identifier(&value.package), "bad flag package");
|
ensure!(codegen::is_valid_package_ident(&value.package), "bad flag package");
|
||||||
ensure!(codegen::is_valid_identifier(&value.name), "bad flag name");
|
ensure!(codegen::is_valid_name_ident(&value.name), "bad flag name");
|
||||||
ensure!(
|
ensure!(
|
||||||
value.package == self.cache.package,
|
value.package == self.cache.package,
|
||||||
"failed to set values for flag {}/{} from {}: expected package {}",
|
"failed to set values for flag {}/{} from {}: expected package {}",
|
||||||
@@ -182,7 +182,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_add_flag_declaration() {
|
fn test_add_flag_declaration() {
|
||||||
let mut builder = CacheBuilder::new("ns".to_string()).unwrap();
|
let mut builder = CacheBuilder::new("com.example".to_string()).unwrap();
|
||||||
builder
|
builder
|
||||||
.add_flag_declaration(
|
.add_flag_declaration(
|
||||||
Source::File("first.txt".to_string()),
|
Source::File("first.txt".to_string()),
|
||||||
@@ -217,12 +217,12 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_add_flag_value() {
|
fn test_add_flag_value() {
|
||||||
let mut builder = CacheBuilder::new("ns".to_string()).unwrap();
|
let mut builder = CacheBuilder::new("com.example".to_string()).unwrap();
|
||||||
let error = builder
|
let error = builder
|
||||||
.add_flag_value(
|
.add_flag_value(
|
||||||
Source::Memory,
|
Source::Memory,
|
||||||
FlagValue {
|
FlagValue {
|
||||||
package: "ns".to_string(),
|
package: "com.example".to_string(),
|
||||||
name: "foo".to_string(),
|
name: "foo".to_string(),
|
||||||
state: FlagState::Enabled,
|
state: FlagState::Enabled,
|
||||||
permission: Permission::ReadOnly,
|
permission: Permission::ReadOnly,
|
||||||
@@ -231,7 +231,7 @@ mod tests {
|
|||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&format!("{:?}", error),
|
&format!("{:?}", error),
|
||||||
"failed to set values for flag ns/foo from <memory>: flag not declared"
|
"failed to set values for flag com.example/foo from <memory>: flag not declared"
|
||||||
);
|
);
|
||||||
|
|
||||||
builder
|
builder
|
||||||
@@ -245,7 +245,7 @@ mod tests {
|
|||||||
.add_flag_value(
|
.add_flag_value(
|
||||||
Source::Memory,
|
Source::Memory,
|
||||||
FlagValue {
|
FlagValue {
|
||||||
package: "ns".to_string(),
|
package: "com.example".to_string(),
|
||||||
name: "foo".to_string(),
|
name: "foo".to_string(),
|
||||||
state: FlagState::Disabled,
|
state: FlagState::Disabled,
|
||||||
permission: Permission::ReadOnly,
|
permission: Permission::ReadOnly,
|
||||||
@@ -257,7 +257,7 @@ mod tests {
|
|||||||
.add_flag_value(
|
.add_flag_value(
|
||||||
Source::Memory,
|
Source::Memory,
|
||||||
FlagValue {
|
FlagValue {
|
||||||
package: "ns".to_string(),
|
package: "com.example".to_string(),
|
||||||
name: "foo".to_string(),
|
name: "foo".to_string(),
|
||||||
state: FlagState::Enabled,
|
state: FlagState::Enabled,
|
||||||
permission: Permission::ReadWrite,
|
permission: Permission::ReadWrite,
|
||||||
@@ -277,7 +277,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
assert_eq!(&format!("{:?}", error), "failed to set values for flag some_other_package/foo from <memory>: expected package ns");
|
assert_eq!(&format!("{:?}", error), "failed to set values for flag some_other_package/foo from <memory>: expected package com.example");
|
||||||
|
|
||||||
let cache = builder.build();
|
let cache = builder.build();
|
||||||
let item = cache.iter().find(|&item| item.name == "foo").unwrap();
|
let item = cache.iter().find(|&item| item.name == "foo").unwrap();
|
||||||
@@ -292,7 +292,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_reject_empty_flag_declaration_fields() {
|
fn test_reject_empty_flag_declaration_fields() {
|
||||||
let mut builder = CacheBuilder::new("ns".to_string()).unwrap();
|
let mut builder = CacheBuilder::new("com.example".to_string()).unwrap();
|
||||||
|
|
||||||
let error = builder
|
let error = builder
|
||||||
.add_flag_declaration(
|
.add_flag_declaration(
|
||||||
@@ -313,7 +313,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_reject_empty_flag_value_files() {
|
fn test_reject_empty_flag_value_files() {
|
||||||
let mut builder = CacheBuilder::new("ns".to_string()).unwrap();
|
let mut builder = CacheBuilder::new("com.example".to_string()).unwrap();
|
||||||
builder
|
builder
|
||||||
.add_flag_declaration(
|
.add_flag_declaration(
|
||||||
Source::Memory,
|
Source::Memory,
|
||||||
@@ -338,7 +338,7 @@ mod tests {
|
|||||||
.add_flag_value(
|
.add_flag_value(
|
||||||
Source::Memory,
|
Source::Memory,
|
||||||
FlagValue {
|
FlagValue {
|
||||||
package: "ns".to_string(),
|
package: "com.example".to_string(),
|
||||||
name: "".to_string(),
|
name: "".to_string(),
|
||||||
state: FlagState::Enabled,
|
state: FlagState::Enabled,
|
||||||
permission: Permission::ReadOnly,
|
permission: Permission::ReadOnly,
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub fn is_valid_identifier(s: &str) -> bool {
|
pub fn is_valid_name_ident(s: &str) -> bool {
|
||||||
// Identifiers must match [a-z][a-z0-9_]*
|
// Identifiers must match [a-z][a-z0-9_]*
|
||||||
let mut chars = s.chars();
|
let mut chars = s.chars();
|
||||||
let Some(first) = chars.next() else {
|
let Some(first) = chars.next() else {
|
||||||
@@ -26,18 +26,40 @@ pub fn is_valid_identifier(s: &str) -> bool {
|
|||||||
chars.all(|ch| ch.is_ascii_lowercase() || ch.is_ascii_digit() || ch == '_')
|
chars.all(|ch| ch.is_ascii_lowercase() || ch.is_ascii_digit() || ch == '_')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_valid_package_ident(s: &str) -> bool {
|
||||||
|
s.split('.').all(is_valid_name_ident)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_valid_identifier() {
|
fn test_is_valid_name_ident() {
|
||||||
assert!(is_valid_identifier("foo"));
|
assert!(is_valid_name_ident("foo"));
|
||||||
assert!(is_valid_identifier("foo_bar_123"));
|
assert!(is_valid_name_ident("foo_bar_123"));
|
||||||
|
|
||||||
assert!(!is_valid_identifier(""));
|
assert!(!is_valid_name_ident(""));
|
||||||
assert!(!is_valid_identifier("123_foo"));
|
assert!(!is_valid_name_ident("123_foo"));
|
||||||
assert!(!is_valid_identifier("foo-bar"));
|
assert!(!is_valid_name_ident("foo-bar"));
|
||||||
assert!(!is_valid_identifier("foo-b\u{00e5}r"));
|
assert!(!is_valid_name_ident("foo-b\u{00e5}r"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_valid_package_ident() {
|
||||||
|
assert!(is_valid_package_ident("foo"));
|
||||||
|
assert!(is_valid_package_ident("foo_bar_123"));
|
||||||
|
assert!(is_valid_package_ident("foo.bar"));
|
||||||
|
assert!(is_valid_package_ident("foo.bar.a123"));
|
||||||
|
|
||||||
|
assert!(!is_valid_package_ident(""));
|
||||||
|
assert!(!is_valid_package_ident("123_foo"));
|
||||||
|
assert!(!is_valid_package_ident("foo-bar"));
|
||||||
|
assert!(!is_valid_package_ident("foo-b\u{00e5}r"));
|
||||||
|
assert!(!is_valid_package_ident("foo.bar.123"));
|
||||||
|
assert!(!is_valid_package_ident(".foo.bar"));
|
||||||
|
assert!(!is_valid_package_ident("foo.bar."));
|
||||||
|
assert!(!is_valid_package_ident("."));
|
||||||
|
assert!(!is_valid_package_ident("foo..bar"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -14,28 +14,35 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::{ensure, Result};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use tinytemplate::TinyTemplate;
|
use tinytemplate::TinyTemplate;
|
||||||
|
|
||||||
use crate::aconfig::{FlagState, Permission};
|
use crate::aconfig::{FlagState, Permission};
|
||||||
use crate::cache::{Cache, Item};
|
use crate::cache::{Cache, Item};
|
||||||
|
use crate::codegen;
|
||||||
use crate::commands::OutputFile;
|
use crate::commands::OutputFile;
|
||||||
|
|
||||||
pub fn generate_cpp_code(cache: &Cache) -> Result<OutputFile> {
|
pub fn generate_cpp_code(cache: &Cache) -> Result<OutputFile> {
|
||||||
let class_elements: Vec<ClassElement> = cache.iter().map(create_class_element).collect();
|
let class_elements: Vec<ClassElement> = cache.iter().map(create_class_element).collect();
|
||||||
let readwrite = class_elements.iter().any(|item| item.readwrite);
|
let readwrite = class_elements.iter().any(|item| item.readwrite);
|
||||||
let package = cache.package().to_lowercase();
|
let package = cache.package().to_string();
|
||||||
let context = Context { package: package.clone(), readwrite, class_elements };
|
let header = package.replace('.', "_");
|
||||||
|
let cpp_namespace = package.replace('.', "::");
|
||||||
|
ensure!(codegen::is_valid_name_ident(&header));
|
||||||
|
let context =
|
||||||
|
Context { header: header.clone(), cpp_namespace, package, readwrite, class_elements };
|
||||||
let mut template = TinyTemplate::new();
|
let mut template = TinyTemplate::new();
|
||||||
template.add_template("cpp_code_gen", include_str!("../templates/cpp.template"))?;
|
template.add_template("cpp_code_gen", include_str!("../templates/cpp.template"))?;
|
||||||
let contents = template.render("cpp_code_gen", &context)?;
|
let contents = template.render("cpp_code_gen", &context)?;
|
||||||
let path = ["aconfig", &(package + ".h")].iter().collect();
|
let path = ["aconfig", &(header + ".h")].iter().collect();
|
||||||
Ok(OutputFile { contents: contents.into(), path })
|
Ok(OutputFile { contents: contents.into(), path })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct Context {
|
struct Context {
|
||||||
|
pub header: String,
|
||||||
|
pub cpp_namespace: String,
|
||||||
pub package: String,
|
pub package: String,
|
||||||
pub readwrite: bool,
|
pub readwrite: bool,
|
||||||
pub class_elements: Vec<ClassElement>,
|
pub class_elements: Vec<ClassElement>,
|
||||||
@@ -69,7 +76,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_cpp_codegen_build_time_flag_only() {
|
fn test_cpp_codegen_build_time_flag_only() {
|
||||||
let package = "my_package";
|
let package = "com.example";
|
||||||
let mut builder = CacheBuilder::new(package.to_string()).unwrap();
|
let mut builder = CacheBuilder::new(package.to_string()).unwrap();
|
||||||
builder
|
builder
|
||||||
.add_flag_declaration(
|
.add_flag_declaration(
|
||||||
@@ -109,11 +116,10 @@ mod tests {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let cache = builder.build();
|
let cache = builder.build();
|
||||||
let expect_content = r#"#ifndef my_package_HEADER_H
|
let expect_content = r#"#ifndef com_example_HEADER_H
|
||||||
#define my_package_HEADER_H
|
#define com_example_HEADER_H
|
||||||
#include "my_package.h"
|
|
||||||
|
|
||||||
namespace my_package {
|
namespace com::example {
|
||||||
|
|
||||||
class my_flag_one {
|
class my_flag_one {
|
||||||
public:
|
public:
|
||||||
@@ -133,7 +139,7 @@ mod tests {
|
|||||||
#endif
|
#endif
|
||||||
"#;
|
"#;
|
||||||
let file = generate_cpp_code(&cache).unwrap();
|
let file = generate_cpp_code(&cache).unwrap();
|
||||||
assert_eq!("aconfig/my_package.h", file.path.to_str().unwrap());
|
assert_eq!("aconfig/com_example.h", file.path.to_str().unwrap());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expect_content.replace(' ', ""),
|
expect_content.replace(' ', ""),
|
||||||
String::from_utf8(file.contents).unwrap().replace(' ', "")
|
String::from_utf8(file.contents).unwrap().replace(' ', "")
|
||||||
@@ -142,7 +148,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_cpp_codegen_runtime_flag() {
|
fn test_cpp_codegen_runtime_flag() {
|
||||||
let package = "my_package";
|
let package = "com.example";
|
||||||
let mut builder = CacheBuilder::new(package.to_string()).unwrap();
|
let mut builder = CacheBuilder::new(package.to_string()).unwrap();
|
||||||
builder
|
builder
|
||||||
.add_flag_declaration(
|
.add_flag_declaration(
|
||||||
@@ -172,20 +178,19 @@ mod tests {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let cache = builder.build();
|
let cache = builder.build();
|
||||||
let expect_content = r#"#ifndef my_package_HEADER_H
|
let expect_content = r#"#ifndef com_example_HEADER_H
|
||||||
#define my_package_HEADER_H
|
#define com_example_HEADER_H
|
||||||
#include "my_package.h"
|
|
||||||
|
|
||||||
#include <server_configurable_flags/get_flags.h>
|
#include <server_configurable_flags/get_flags.h>
|
||||||
using namespace server_configurable_flags;
|
using namespace server_configurable_flags;
|
||||||
|
|
||||||
namespace my_package {
|
namespace com::example {
|
||||||
|
|
||||||
class my_flag_one {
|
class my_flag_one {
|
||||||
public:
|
public:
|
||||||
virtual const bool value() {
|
virtual const bool value() {
|
||||||
return GetServerConfigurableFlag(
|
return GetServerConfigurableFlag(
|
||||||
"my_package",
|
"com.example",
|
||||||
"my_flag_one",
|
"my_flag_one",
|
||||||
"false") == "true";
|
"false") == "true";
|
||||||
}
|
}
|
||||||
@@ -195,7 +200,7 @@ mod tests {
|
|||||||
public:
|
public:
|
||||||
virtual const bool value() {
|
virtual const bool value() {
|
||||||
return GetServerConfigurableFlag(
|
return GetServerConfigurableFlag(
|
||||||
"my_package",
|
"com.example",
|
||||||
"my_flag_two",
|
"my_flag_two",
|
||||||
"true") == "true";
|
"true") == "true";
|
||||||
}
|
}
|
||||||
@@ -205,7 +210,7 @@ mod tests {
|
|||||||
#endif
|
#endif
|
||||||
"#;
|
"#;
|
||||||
let file = generate_cpp_code(&cache).unwrap();
|
let file = generate_cpp_code(&cache).unwrap();
|
||||||
assert_eq!("aconfig/my_package.h", file.path.to_str().unwrap());
|
assert_eq!("aconfig/com_example.h", file.path.to_str().unwrap());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expect_content.replace(' ', ""),
|
expect_content.replace(' ', ""),
|
||||||
String::from_utf8(file.contents).unwrap().replace(' ', "")
|
String::from_utf8(file.contents).unwrap().replace(' ', "")
|
||||||
|
@@ -31,7 +31,7 @@ pub fn generate_java_code(cache: &Cache) -> Result<OutputFile> {
|
|||||||
let mut template = TinyTemplate::new();
|
let mut template = TinyTemplate::new();
|
||||||
template.add_template("java_code_gen", include_str!("../templates/java.template"))?;
|
template.add_template("java_code_gen", include_str!("../templates/java.template"))?;
|
||||||
let contents = template.render("java_code_gen", &context)?;
|
let contents = template.render("java_code_gen", &context)?;
|
||||||
let mut path: PathBuf = ["aconfig", package].iter().collect();
|
let mut path: PathBuf = package.split('.').collect();
|
||||||
// TODO: Allow customization of the java class name
|
// TODO: Allow customization of the java class name
|
||||||
path.push("Flags.java");
|
path.push("Flags.java");
|
||||||
Ok(OutputFile { contents: contents.into(), path })
|
Ok(OutputFile { contents: contents.into(), path })
|
||||||
@@ -76,7 +76,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_generate_java_code() {
|
fn test_generate_java_code() {
|
||||||
let package = "example";
|
let package = "com.example";
|
||||||
let mut builder = CacheBuilder::new(package.to_string()).unwrap();
|
let mut builder = CacheBuilder::new(package.to_string()).unwrap();
|
||||||
builder
|
builder
|
||||||
.add_flag_declaration(
|
.add_flag_declaration(
|
||||||
@@ -106,7 +106,7 @@ mod tests {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let cache = builder.build();
|
let cache = builder.build();
|
||||||
let expect_content = r#"package aconfig.example;
|
let expect_content = r#"package com.example;
|
||||||
|
|
||||||
import android.provider.DeviceConfig;
|
import android.provider.DeviceConfig;
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@ mod tests {
|
|||||||
|
|
||||||
public static boolean test2() {
|
public static boolean test2() {
|
||||||
return DeviceConfig.getBoolean(
|
return DeviceConfig.getBoolean(
|
||||||
"example",
|
"com.example",
|
||||||
"test2__test2",
|
"test2__test2",
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
@@ -127,7 +127,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
let file = generate_java_code(&cache).unwrap();
|
let file = generate_java_code(&cache).unwrap();
|
||||||
assert_eq!("aconfig/example/Flags.java", file.path.to_str().unwrap());
|
assert_eq!("com/example/Flags.java", file.path.to_str().unwrap());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expect_content.replace(' ', ""),
|
expect_content.replace(' ', ""),
|
||||||
String::from_utf8(file.contents).unwrap().replace(' ', "")
|
String::from_utf8(file.contents).unwrap().replace(' ', "")
|
||||||
|
@@ -24,9 +24,12 @@ use crate::commands::OutputFile;
|
|||||||
|
|
||||||
pub fn generate_rust_code(cache: &Cache) -> Result<OutputFile> {
|
pub fn generate_rust_code(cache: &Cache) -> Result<OutputFile> {
|
||||||
let package = cache.package();
|
let package = cache.package();
|
||||||
let parsed_flags: Vec<TemplateParsedFlag> =
|
let parsed_flags: Vec<TemplateParsedFlag> = cache.iter().map(|item| item.into()).collect();
|
||||||
cache.iter().map(|item| create_template_parsed_flag(package, item)).collect();
|
let context = TemplateContext {
|
||||||
let context = TemplateContext { package: package.to_string(), parsed_flags };
|
package: package.to_string(),
|
||||||
|
parsed_flags,
|
||||||
|
modules: package.split('.').map(|s| s.to_string()).collect::<Vec<_>>(),
|
||||||
|
};
|
||||||
let mut template = TinyTemplate::new();
|
let mut template = TinyTemplate::new();
|
||||||
template.add_template("rust_code_gen", include_str!("../templates/rust.template"))?;
|
template.add_template("rust_code_gen", include_str!("../templates/rust.template"))?;
|
||||||
let contents = template.render("rust_code_gen", &context)?;
|
let contents = template.render("rust_code_gen", &context)?;
|
||||||
@@ -38,12 +41,12 @@ pub fn generate_rust_code(cache: &Cache) -> Result<OutputFile> {
|
|||||||
struct TemplateContext {
|
struct TemplateContext {
|
||||||
pub package: String,
|
pub package: String,
|
||||||
pub parsed_flags: Vec<TemplateParsedFlag>,
|
pub parsed_flags: Vec<TemplateParsedFlag>,
|
||||||
|
pub modules: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct TemplateParsedFlag {
|
struct TemplateParsedFlag {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub fn_name: String,
|
|
||||||
|
|
||||||
// TinyTemplate's conditionals are limited to single <bool> expressions; list all options here
|
// TinyTemplate's conditionals are limited to single <bool> expressions; list all options here
|
||||||
// Invariant: exactly one of these fields will be true
|
// Invariant: exactly one of these fields will be true
|
||||||
@@ -52,11 +55,11 @@ struct TemplateParsedFlag {
|
|||||||
pub is_read_write: bool,
|
pub is_read_write: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::nonminimal_bool)]
|
impl From<&Item> for TemplateParsedFlag {
|
||||||
fn create_template_parsed_flag(package: &str, item: &Item) -> TemplateParsedFlag {
|
#[allow(clippy::nonminimal_bool)]
|
||||||
|
fn from(item: &Item) -> Self {
|
||||||
let template = TemplateParsedFlag {
|
let template = TemplateParsedFlag {
|
||||||
name: item.name.clone(),
|
name: item.name.clone(),
|
||||||
fn_name: format!("{}_{}", package, &item.name),
|
|
||||||
is_read_only_enabled: item.permission == Permission::ReadOnly
|
is_read_only_enabled: item.permission == Permission::ReadOnly
|
||||||
&& item.state == FlagState::Enabled,
|
&& item.state == FlagState::Enabled,
|
||||||
is_read_only_disabled: item.permission == Permission::ReadOnly
|
is_read_only_disabled: item.permission == Permission::ReadOnly
|
||||||
@@ -74,6 +77,7 @@ fn create_template_parsed_flag(package: &str, item: &Item) -> TemplateParsedFlag
|
|||||||
template.is_read_write,
|
template.is_read_write,
|
||||||
);
|
);
|
||||||
template
|
template
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -86,24 +90,33 @@ mod tests {
|
|||||||
let generated = generate_rust_code(&cache).unwrap();
|
let generated = generate_rust_code(&cache).unwrap();
|
||||||
assert_eq!("src/lib.rs", format!("{}", generated.path.display()));
|
assert_eq!("src/lib.rs", format!("{}", generated.path.display()));
|
||||||
let expected = r#"
|
let expected = r#"
|
||||||
|
pub mod com {
|
||||||
|
pub mod android {
|
||||||
|
pub mod aconfig {
|
||||||
|
pub mod test {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub const fn r#test_disabled_ro() -> bool {
|
pub const fn r#disabled_ro() -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn r#test_disabled_rw() -> bool {
|
pub fn r#disabled_rw() -> bool {
|
||||||
flags_rust::GetServerConfigurableFlag("test", "disabled_rw", "false") == "true"
|
flags_rust::GetServerConfigurableFlag("com.android.aconfig.test", "disabled_rw", "false") == "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub const fn r#test_enabled_ro() -> bool {
|
pub const fn r#enabled_ro() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn r#test_enabled_rw() -> bool {
|
pub fn r#enabled_rw() -> bool {
|
||||||
flags_rust::GetServerConfigurableFlag("test", "enabled_rw", "false") == "true"
|
flags_rust::GetServerConfigurableFlag("com.android.aconfig.test", "enabled_rw", "false") == "true"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
assert_eq!(expected.trim(), String::from_utf8(generated.contents).unwrap().trim());
|
assert_eq!(expected.trim(), String::from_utf8(generated.contents).unwrap().trim());
|
||||||
|
@@ -186,9 +186,9 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::aconfig::{FlagState, Permission};
|
use crate::aconfig::{FlagState, Permission};
|
||||||
|
|
||||||
fn create_test_cache_ns1() -> Cache {
|
fn create_test_cache_com_example() -> Cache {
|
||||||
let s = r#"
|
let s = r#"
|
||||||
package: "ns1"
|
package: "com.example"
|
||||||
flag {
|
flag {
|
||||||
name: "a"
|
name: "a"
|
||||||
description: "Description of a"
|
description: "Description of a"
|
||||||
@@ -201,19 +201,19 @@ mod tests {
|
|||||||
let declarations = vec![Input { source: Source::Memory, reader: Box::new(s.as_bytes()) }];
|
let declarations = vec![Input { source: Source::Memory, reader: Box::new(s.as_bytes()) }];
|
||||||
let o = r#"
|
let o = r#"
|
||||||
flag_value {
|
flag_value {
|
||||||
package: "ns1"
|
package: "com.example"
|
||||||
name: "a"
|
name: "a"
|
||||||
state: DISABLED
|
state: DISABLED
|
||||||
permission: READ_ONLY
|
permission: READ_ONLY
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
let values = vec![Input { source: Source::Memory, reader: Box::new(o.as_bytes()) }];
|
let values = vec![Input { source: Source::Memory, reader: Box::new(o.as_bytes()) }];
|
||||||
create_cache("ns1", declarations, values).unwrap()
|
create_cache("com.example", declarations, values).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_test_cache_ns2() -> Cache {
|
fn create_test_cache_com_other() -> Cache {
|
||||||
let s = r#"
|
let s = r#"
|
||||||
package: "ns2"
|
package: "com.other"
|
||||||
flag {
|
flag {
|
||||||
name: "c"
|
name: "c"
|
||||||
description: "Description of c"
|
description: "Description of c"
|
||||||
@@ -222,19 +222,19 @@ mod tests {
|
|||||||
let declarations = vec![Input { source: Source::Memory, reader: Box::new(s.as_bytes()) }];
|
let declarations = vec![Input { source: Source::Memory, reader: Box::new(s.as_bytes()) }];
|
||||||
let o = r#"
|
let o = r#"
|
||||||
flag_value {
|
flag_value {
|
||||||
package: "ns2"
|
package: "com.other"
|
||||||
name: "c"
|
name: "c"
|
||||||
state: DISABLED
|
state: DISABLED
|
||||||
permission: READ_ONLY
|
permission: READ_ONLY
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
let values = vec![Input { source: Source::Memory, reader: Box::new(o.as_bytes()) }];
|
let values = vec![Input { source: Source::Memory, reader: Box::new(o.as_bytes()) }];
|
||||||
create_cache("ns2", declarations, values).unwrap()
|
create_cache("com.other", declarations, values).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_cache() {
|
fn test_create_cache() {
|
||||||
let caches = create_test_cache_ns1(); // calls create_cache
|
let caches = create_test_cache_com_example(); // calls create_cache
|
||||||
let item = caches.iter().find(|&item| item.name == "a").unwrap();
|
let item = caches.iter().find(|&item| item.name == "a").unwrap();
|
||||||
assert_eq!(FlagState::Disabled, item.state);
|
assert_eq!(FlagState::Disabled, item.state);
|
||||||
assert_eq!(Permission::ReadOnly, item.permission);
|
assert_eq!(Permission::ReadOnly, item.permission);
|
||||||
@@ -245,7 +245,7 @@ mod tests {
|
|||||||
let caches = vec![crate::test::create_cache()];
|
let caches = vec![crate::test::create_cache()];
|
||||||
let bytes = create_device_config_defaults(caches).unwrap();
|
let bytes = create_device_config_defaults(caches).unwrap();
|
||||||
let text = std::str::from_utf8(&bytes).unwrap();
|
let text = std::str::from_utf8(&bytes).unwrap();
|
||||||
assert_eq!("test/disabled_rw:disabled\ntest/enabled_rw:enabled\n", text);
|
assert_eq!("com.android.aconfig.test/disabled_rw:disabled\ncom.android.aconfig.test/enabled_rw:enabled\n", text);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -253,12 +253,12 @@ mod tests {
|
|||||||
let caches = vec![crate::test::create_cache()];
|
let caches = vec![crate::test::create_cache()];
|
||||||
let bytes = create_device_config_sysprops(caches).unwrap();
|
let bytes = create_device_config_sysprops(caches).unwrap();
|
||||||
let text = std::str::from_utf8(&bytes).unwrap();
|
let text = std::str::from_utf8(&bytes).unwrap();
|
||||||
assert_eq!("persist.device_config.test.disabled_rw=false\npersist.device_config.test.enabled_rw=true\n", text);
|
assert_eq!("persist.device_config.com.android.aconfig.test.disabled_rw=false\npersist.device_config.com.android.aconfig.test.enabled_rw=true\n", text);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_dump_text_format() {
|
fn test_dump_text_format() {
|
||||||
let caches = vec![create_test_cache_ns1()];
|
let caches = vec![create_test_cache_com_example()];
|
||||||
let bytes = dump_cache(caches, DumpFormat::Text).unwrap();
|
let bytes = dump_cache(caches, DumpFormat::Text).unwrap();
|
||||||
let text = std::str::from_utf8(&bytes).unwrap();
|
let text = std::str::from_utf8(&bytes).unwrap();
|
||||||
assert!(text.contains("a: Disabled"));
|
assert!(text.contains("a: Disabled"));
|
||||||
@@ -269,7 +269,7 @@ mod tests {
|
|||||||
use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoTracepoint};
|
use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoTracepoint};
|
||||||
use protobuf::Message;
|
use protobuf::Message;
|
||||||
|
|
||||||
let caches = vec![create_test_cache_ns1()];
|
let caches = vec![create_test_cache_com_example()];
|
||||||
let bytes = dump_cache(caches, DumpFormat::Protobuf).unwrap();
|
let bytes = dump_cache(caches, DumpFormat::Protobuf).unwrap();
|
||||||
let actual = ProtoParsedFlags::parse_from_bytes(&bytes).unwrap();
|
let actual = ProtoParsedFlags::parse_from_bytes(&bytes).unwrap();
|
||||||
|
|
||||||
@@ -280,7 +280,7 @@ mod tests {
|
|||||||
|
|
||||||
let item =
|
let item =
|
||||||
actual.parsed_flag.iter().find(|item| item.name == Some("b".to_string())).unwrap();
|
actual.parsed_flag.iter().find(|item| item.name == Some("b".to_string())).unwrap();
|
||||||
assert_eq!(item.package(), "ns1");
|
assert_eq!(item.package(), "com.example");
|
||||||
assert_eq!(item.name(), "b");
|
assert_eq!(item.name(), "b");
|
||||||
assert_eq!(item.description(), "Description of b");
|
assert_eq!(item.description(), "Description of b");
|
||||||
assert_eq!(item.state(), ProtoFlagState::DISABLED);
|
assert_eq!(item.state(), ProtoFlagState::DISABLED);
|
||||||
@@ -294,7 +294,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_dump_multiple_caches() {
|
fn test_dump_multiple_caches() {
|
||||||
let caches = vec![create_test_cache_ns1(), create_test_cache_ns2()];
|
let caches = vec![create_test_cache_com_example(), create_test_cache_com_other()];
|
||||||
let bytes = dump_cache(caches, DumpFormat::Protobuf).unwrap();
|
let bytes = dump_cache(caches, DumpFormat::Protobuf).unwrap();
|
||||||
let dump = ProtoParsedFlags::parse_from_bytes(&bytes).unwrap();
|
let dump = ProtoParsedFlags::parse_from_bytes(&bytes).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -302,10 +302,14 @@ mod tests {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|parsed_flag| format!("{}/{}", parsed_flag.package(), parsed_flag.name()))
|
.map(|parsed_flag| format!("{}/{}", parsed_flag.package(), parsed_flag.name()))
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
vec!["ns1/a".to_string(), "ns1/b".to_string(), "ns2/c".to_string()]
|
vec![
|
||||||
|
"com.example/a".to_string(),
|
||||||
|
"com.example/b".to_string(),
|
||||||
|
"com.other/c".to_string()
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
let caches = vec![create_test_cache_ns2(), create_test_cache_ns1()];
|
let caches = vec![create_test_cache_com_other(), create_test_cache_com_example()];
|
||||||
let bytes = dump_cache(caches, DumpFormat::Protobuf).unwrap();
|
let bytes = dump_cache(caches, DumpFormat::Protobuf).unwrap();
|
||||||
let dump_reversed_input = ProtoParsedFlags::parse_from_bytes(&bytes).unwrap();
|
let dump_reversed_input = ProtoParsedFlags::parse_from_bytes(&bytes).unwrap();
|
||||||
assert_eq!(dump, dump_reversed_input);
|
assert_eq!(dump, dump_reversed_input);
|
||||||
|
@@ -21,7 +21,7 @@ pub mod test_utils {
|
|||||||
|
|
||||||
pub fn create_cache() -> Cache {
|
pub fn create_cache() -> Cache {
|
||||||
crate::commands::create_cache(
|
crate::commands::create_cache(
|
||||||
"test",
|
"com.android.aconfig.test",
|
||||||
vec![Input {
|
vec![Input {
|
||||||
source: Source::File("testdata/test.aconfig".to_string()),
|
source: Source::File("testdata/test.aconfig".to_string()),
|
||||||
reader: Box::new(include_bytes!("../testdata/test.aconfig").as_slice()),
|
reader: Box::new(include_bytes!("../testdata/test.aconfig").as_slice()),
|
||||||
|
@@ -1,11 +1,10 @@
|
|||||||
#ifndef {package}_HEADER_H
|
#ifndef {header}_HEADER_H
|
||||||
#define {package}_HEADER_H
|
#define {header}_HEADER_H
|
||||||
#include "{package}.h"
|
|
||||||
{{ if readwrite }}
|
{{ if readwrite }}
|
||||||
#include <server_configurable_flags/get_flags.h>
|
#include <server_configurable_flags/get_flags.h>
|
||||||
using namespace server_configurable_flags;
|
using namespace server_configurable_flags;
|
||||||
{{ endif }}
|
{{ endif }}
|
||||||
namespace {package} \{
|
namespace {cpp_namespace} \{
|
||||||
{{ for item in class_elements}}
|
{{ for item in class_elements}}
|
||||||
class {item.flag_name} \{
|
class {item.flag_name} \{
|
||||||
public:
|
public:
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
package aconfig.{package};
|
package {package};
|
||||||
{{ if readwrite }}
|
{{ if readwrite }}
|
||||||
import android.provider.DeviceConfig;
|
import android.provider.DeviceConfig;
|
||||||
{{ endif }}
|
{{ endif }}
|
||||||
|
@@ -1,23 +1,29 @@
|
|||||||
|
{{- for mod in modules -}}
|
||||||
|
pub mod {mod} \{
|
||||||
|
{{ endfor -}}
|
||||||
{{- for parsed_flag in parsed_flags -}}
|
{{- for parsed_flag in parsed_flags -}}
|
||||||
{{- if parsed_flag.is_read_only_disabled -}}
|
{{- if parsed_flag.is_read_only_disabled -}}
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub const fn r#{parsed_flag.fn_name}() -> bool \{
|
pub const fn r#{parsed_flag.name}() -> bool \{
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
{{ endif -}}
|
{{ endif -}}
|
||||||
{{- if parsed_flag.is_read_only_enabled -}}
|
{{- if parsed_flag.is_read_only_enabled -}}
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub const fn r#{parsed_flag.fn_name}() -> bool \{
|
pub const fn r#{parsed_flag.name}() -> bool \{
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
{{ endif -}}
|
{{ endif -}}
|
||||||
{{- if parsed_flag.is_read_write -}}
|
{{- if parsed_flag.is_read_write -}}
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn r#{parsed_flag.fn_name}() -> bool \{
|
pub fn r#{parsed_flag.name}() -> bool \{
|
||||||
flags_rust::GetServerConfigurableFlag("{package}", "{parsed_flag.name}", "false") == "true"
|
flags_rust::GetServerConfigurableFlag("{package}", "{parsed_flag.name}", "false") == "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
{{ endif -}}
|
{{ endif -}}
|
||||||
{{- endfor -}}
|
{{- endfor -}}
|
||||||
|
{{- for mod in modules -}}
|
||||||
|
}
|
||||||
|
{{ endfor -}}
|
||||||
|
6
tools/aconfig/testdata/first.values
vendored
6
tools/aconfig/testdata/first.values
vendored
@@ -1,17 +1,17 @@
|
|||||||
flag_value {
|
flag_value {
|
||||||
package: "test"
|
package: "com.android.aconfig.test"
|
||||||
name: "disabled_ro"
|
name: "disabled_ro"
|
||||||
state: DISABLED
|
state: DISABLED
|
||||||
permission: READ_ONLY
|
permission: READ_ONLY
|
||||||
}
|
}
|
||||||
flag_value {
|
flag_value {
|
||||||
package: "test"
|
package: "com.android.aconfig.test"
|
||||||
name: "enabled_ro"
|
name: "enabled_ro"
|
||||||
state: DISABLED
|
state: DISABLED
|
||||||
permission: READ_WRITE
|
permission: READ_WRITE
|
||||||
}
|
}
|
||||||
flag_value {
|
flag_value {
|
||||||
package: "test"
|
package: "com.android.aconfig.test"
|
||||||
name: "enabled_rw"
|
name: "enabled_rw"
|
||||||
state: ENABLED
|
state: ENABLED
|
||||||
permission: READ_WRITE
|
permission: READ_WRITE
|
||||||
|
2
tools/aconfig/testdata/second.values
vendored
2
tools/aconfig/testdata/second.values
vendored
@@ -1,5 +1,5 @@
|
|||||||
flag_value {
|
flag_value {
|
||||||
package: "test"
|
package: "com.android.aconfig.test"
|
||||||
name: "enabled_ro"
|
name: "enabled_ro"
|
||||||
state: ENABLED
|
state: ENABLED
|
||||||
permission: READ_ONLY
|
permission: READ_ONLY
|
||||||
|
2
tools/aconfig/testdata/test.aconfig
vendored
2
tools/aconfig/testdata/test.aconfig
vendored
@@ -1,4 +1,4 @@
|
|||||||
package: "test"
|
package: "com.android.aconfig.test"
|
||||||
|
|
||||||
# This flag's final value is calculated from:
|
# This flag's final value is calculated from:
|
||||||
# - test.aconfig: DISABLED + READ_WRITE (default)
|
# - test.aconfig: DISABLED + READ_WRITE (default)
|
||||||
|
Reference in New Issue
Block a user