Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

config: support optional references; fix auto saving #146

Merged
merged 2 commits into from
Sep 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions data/test/config_compiler_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ dependency_priorities:
player: bisu
__include: /starcraft/protoss

optional_reference:
__include: nonexistent.yaml:/?
__patch:
- local/nonexistent_patch?
- config_test:/nonexistent_patch?
- nonexistent:/patch?
untouched: true

local:
patch:
battlefields/@next: match point
Expand Down
39 changes: 26 additions & 13 deletions src/rime/config/config_compiler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ struct PendingChild : Dependency {
};

string Reference::repr() const {
return resource_id + ":" + local_path;
return resource_id + ":" + local_path + (optional ? " <optional>" : "");
}

template <class StreamT>
Expand Down Expand Up @@ -131,7 +131,7 @@ bool IncludeReference::Resolve(ConfigCompiler* compiler) {
DLOG(INFO) << "IncludeReference::Resolve(reference = " << reference << ")";
auto item = ResolveReference(compiler, reference);
if (!item) {
return false;
return reference.optional;
}
*target = item;
return true;
Expand All @@ -141,7 +141,7 @@ bool PatchReference::Resolve(ConfigCompiler* compiler) {
DLOG(INFO) << "PatchReference::Resolve(reference = " << reference << ")";
auto item = ResolveReference(compiler, reference);
if (!item) {
return false;
return reference.optional;
}
if (!Is<ConfigMap>(item)) {
LOG(ERROR) << "invalid patch at " << reference;
Expand Down Expand Up @@ -227,14 +227,18 @@ ConfigCompiler::~ConfigCompiler() {
}

Reference ConfigCompiler::CreateReference(const string& qualified_path) {
auto end = qualified_path.find_last_of("?");
bool optional = end != string::npos;
auto separator = qualified_path.find_first_of(":");
string resource_id = resource_resolver_->ToResourceId(
(separator == string::npos || separator == 0) ?
graph_->current_resource_id() : qualified_path.substr(0, separator));
graph_->current_resource_id() :
qualified_path.substr(0, separator));
string local_path = (separator == string::npos) ?
qualified_path :
qualified_path.substr(separator + 1);
return Reference{resource_id, local_path};
qualified_path.substr(0, end) :
qualified_path.substr(separator + 1,
optional ? end - separator - 1 : end);
return Reference{resource_id, local_path, optional};
}

void ConfigCompiler::AddDependency(an<Dependency> dependency) {
Expand Down Expand Up @@ -267,10 +271,8 @@ an<ConfigResource> ConfigCompiler::Compile(const string& file_name) {
auto resource = New<ConfigResource>(resource_id, New<ConfigData>());
graph_->resources[resource_id] = resource;
graph_->Push(resource, resource_id + ":");
if (!resource->data->LoadFromFile(
resource_resolver_->ResolvePath(resource_id).string(), this)) {
resource.reset();
}
resource->loaded = resource->data->LoadFromFile(
resource_resolver_->ResolvePath(resource_id).string(), this);
graph_->Pop();
return resource;
}
Expand Down Expand Up @@ -356,8 +358,16 @@ static an<ConfigItem> ResolveReference(ConfigCompiler* compiler,
const Reference& reference) {
auto resource = compiler->GetCompiledResource(reference.resource_id);
if (!resource) {
LOG(INFO) << "resource not loaded, compiling: " << reference.resource_id;
DLOG(INFO) << "resource not loaded, compiling: " << reference.resource_id;
resource = compiler->Compile(reference.resource_id);
if (!resource->loaded) {
if (reference.optional) {
LOG(INFO) << "optional resource not loaded: " << reference.resource_id;
} else {
LOG(ERROR) << "resource could not be loaded: " << reference.resource_id;
}
return nullptr;
}
}
return GetResolvedItem(compiler, resource, reference.local_path);
}
Expand Down Expand Up @@ -439,6 +449,9 @@ bool ConfigCompiler::Link(an<ConfigResource> target) {

bool ConfigCompiler::ResolveDependencies(const string& path) {
DLOG(INFO) << "ResolveDependencies(" << path << ")";
if (!graph_->deps.count(path)) {
return true;
}
auto& deps = graph_->deps[path];
for (auto iter = deps.begin(); iter != deps.end(); ) {
if (!(*iter)->Resolve(this)) {
Expand All @@ -448,7 +461,7 @@ bool ConfigCompiler::ResolveDependencies(const string& path) {
LOG(INFO) << "resolved: " << **iter;
iter = deps.erase(iter);
}
LOG(INFO) << "all dependencies resolved.";
DLOG(INFO) << "all dependencies resolved.";
return true;
}

Expand Down
2 changes: 2 additions & 0 deletions src/rime/config/config_compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace rime {
struct ConfigResource : ConfigItemRef {
string resource_id;
an<ConfigData> data;
bool loaded = false;

ConfigResource(const string& _id, an<ConfigData> _data)
: ConfigItemRef(nullptr), resource_id(_id), data(_data) {
Expand All @@ -29,6 +30,7 @@ struct ConfigResource : ConfigItemRef {
struct Reference {
string resource_id;
string local_path;
bool optional;

string repr() const;
};
Expand Down
5 changes: 1 addition & 4 deletions src/rime/config/config_component.cc
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,9 @@ an<ConfigData> ConfigComponent::GetConfigData(const string& file_name) {
if (wp.expired()) { // create a new copy and load it
ConfigCompiler compiler(resource_resolver_.get());
auto resource = compiler.Compile(file_name);
if (!resource || !compiler.Link(resource)) {
if (resource->loaded && !compiler.Link(resource)) {
LOG(ERROR) << "error loading config from: " << file_name;
}
if (!resource) {
return New<ConfigData>();
}
wp = resource->data;
return resource->data;
}
Expand Down
9 changes: 8 additions & 1 deletion test/config_compiler_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,11 @@ TEST_F(RimeConfigCompilerTest, DependencyPriorities) {
EXPECT_EQ("bisu", player);
}

// TODO: test failure cases
TEST_F(RimeConfigCompilerTest, OptionalReference) {
const string& prefix = "optional_reference/";
EXPECT_TRUE(config_->IsNull(prefix + "__include"));
EXPECT_TRUE(config_->IsNull(prefix + "__patch"));
bool untouched;
EXPECT_TRUE(config_->GetBool(prefix + "untouched", &untouched));
EXPECT_TRUE(untouched);
}
19 changes: 15 additions & 4 deletions test/config_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,27 @@ class RimeConfigTest : public ::testing::Test {
the<Config> config_;
};

TEST(RimeConfigComponentTest, RealCreationWorkflow) {
TEST(RimeConfigComponentTest, RoundTrip) {
// registration
Registry& r = Registry::instance();
r.Register("test_config", new ConfigComponent);
// find component
Config::Component* cc = Config::Require("test_config");
ASSERT_TRUE(cc != NULL);
// create Config with id
the<Config> config(cc->Create("config_test"));
EXPECT_TRUE(bool(config));
// create config and write modifications to file
{
the<Config> config(cc->Create("config_round_trip_test"));
EXPECT_TRUE(bool(config));
EXPECT_TRUE(config->SetString("key", "value"));
}
// read from file and verify contents
{
the<Config> config(cc->Create("config_round_trip_test"));
EXPECT_TRUE(bool(config));
string value;
EXPECT_TRUE(config->GetString("key", &value));
EXPECT_EQ("value", value);
}
r.Unregister("test_config");
}

Expand Down