[discover] Provide ModuleOutputWriter service to mods
MI4-2404 #done
TESTED=fx run-test modular_integration_tests -- --gtest_filter='ModuleOutputTest.ModuleWritesToOutput'
Change-Id: I738563aa438408f1f7dea1238880b725eb77fd16
diff --git a/peridot/bin/sessionmgr/BUILD.gn b/peridot/bin/sessionmgr/BUILD.gn
index fe98636..6eff7d7 100644
--- a/peridot/bin/sessionmgr/BUILD.gn
+++ b/peridot/bin/sessionmgr/BUILD.gn
@@ -59,6 +59,7 @@
"//peridot/lib/scoped_tmpfs",
"//peridot/public/lib/app_driver/cpp:app_driver",
"//peridot/public/lib/async/cpp:operation",
+ "//sdk/fidl/fuchsia.app.discover",
"//sdk/fidl/fuchsia.ledger",
"//sdk/fidl/fuchsia.ledger.cloud",
"//sdk/fidl/fuchsia.modular",
diff --git a/peridot/bin/sessionmgr/sessionmgr_impl.cc b/peridot/bin/sessionmgr/sessionmgr_impl.cc
index 14af9c2..ed25ca89 100644
--- a/peridot/bin/sessionmgr/sessionmgr_impl.cc
+++ b/peridot/bin/sessionmgr/sessionmgr_impl.cc
@@ -66,6 +66,9 @@
constexpr char kModuleResolverUrl[] =
"fuchsia-pkg://fuchsia.com/module_resolver#meta/module_resolver.cmx";
+constexpr char kDiscovermgrUrl[] =
+ "fuchsia-pkg://fuchsia.com/discovermgr#meta/discovermgr.cmx";
+
constexpr char kSessionEnvironmentLabelPrefix[] = "session-";
constexpr char kMessageQueuePath[] = "/data/MESSAGE_QUEUES/v1/";
@@ -213,6 +216,7 @@
InitializeLedger(std::move(ledger_token_manager));
InitializeMessageQueueManager();
+ InitializeDiscovermgr();
InitializeMaxwellAndModular(std::move(session_shell_url),
std::move(story_shell_config),
use_session_shell_for_story_shell_factory);
@@ -612,9 +616,9 @@
session_storage_.get(), std::move(story_shell_config),
std::move(story_shell_factory_ptr), component_context_info,
std::move(focus_provider_story_provider),
- user_intelligence_provider_impl_.get(), module_resolver_service_.get(),
- entity_provider_runner_.get(), module_facet_reader_.get(),
- presentation_provider_impl_.get(),
+ user_intelligence_provider_impl_.get(), discover_registry_service_.get(),
+ module_resolver_service_.get(), entity_provider_runner_.get(),
+ module_facet_reader_.get(), presentation_provider_impl_.get(),
startup_context_
->ConnectToEnvironmentService<fuchsia::ui::viewsv1::ViewSnapshot>(),
(config_.enable_story_shell_preload())));
@@ -631,7 +635,7 @@
story_provider_puppet_master.NewRequest();
// Initialize the PuppetMaster.
- // TODO(miguelfrde): there's no clean runtime interface we can inject to
+ // TODO: there's no clean runtime interface we can inject to
// puppet master. Hence, for now we inject this function to be able to focus
// mods. Eventually we want to have a StoryRuntime and SessionRuntime classes
// similar to Story/SessionStorage but for runtime management.
@@ -678,6 +682,41 @@
AtEnd(Reset(&visible_stories_handler_));
}
+// TODO(MI4-2416): pass additional configuration.
+void SessionmgrImpl::InitializeDiscovermgr() {
+ auto service_list = fuchsia::sys::ServiceList::New();
+ service_list->names.push_back(fuchsia::modular::PuppetMaster::Name_);
+ service_list->names.push_back(fuchsia::modular::EntityResolver::Name_);
+ discovermgr_ns_services_.AddService<fuchsia::modular::PuppetMaster>(
+ [this](auto request) {
+ if (terminating_) {
+ return;
+ }
+ puppet_master_impl_->Connect(std::move(request));
+ });
+ discovermgr_ns_services_.AddService<fuchsia::modular::EntityResolver>(
+ [this](auto request) {
+ if (terminating_) {
+ return;
+ }
+ entity_provider_runner_->ConnectEntityResolver(std::move(request));
+ });
+
+ discovermgr_ns_services_.AddBinding(service_list->provider.NewRequest());
+
+ fuchsia::modular::AppConfig discovermgr_config;
+ discovermgr_config.url = kDiscovermgrUrl;
+
+ discovermgr_app_ = std::make_unique<AppClient<fuchsia::modular::Lifecycle>>(
+ session_environment_->GetLauncher(), std::move(discovermgr_config),
+ "" /* data_origin */, std::move(service_list));
+ discovermgr_app_->services().ConnectToService(
+ discover_registry_service_.NewRequest());
+ AtEnd(Reset(&discover_registry_service_));
+ AtEnd(Reset(&discovermgr_app_));
+ AtEnd(Teardown(kBasicTimeout, "Discovermgr", discovermgr_app_.get()));
+}
+
void SessionmgrImpl::InitializeSessionShell(
fuchsia::modular::AppConfig session_shell_config,
fuchsia::ui::views::ViewToken view_token) {
diff --git a/peridot/bin/sessionmgr/sessionmgr_impl.h b/peridot/bin/sessionmgr/sessionmgr_impl.h
index 21e2cee..b484775 100644
--- a/peridot/bin/sessionmgr/sessionmgr_impl.h
+++ b/peridot/bin/sessionmgr/sessionmgr_impl.h
@@ -5,6 +5,7 @@
#ifndef PERIDOT_BIN_SESSIONMGR_SESSIONMGR_IMPL_H_
#define PERIDOT_BIN_SESSIONMGR_SESSIONMGR_IMPL_H_
+#include <fuchsia/app/discover/cpp/fidl.h>
#include <fuchsia/ledger/cloud/cpp/fidl.h>
#include <fuchsia/ledger/cloud/firestore/cpp/fidl.h>
#include <fuchsia/ledger/cpp/fidl.h>
@@ -101,6 +102,7 @@
const fidl::StringPtr& session_shell_url,
fuchsia::modular::AppConfig story_shell_config,
bool use_session_shell_for_story_shell_factory);
+ void InitializeDiscovermgr();
void InitializeSessionShell(fuchsia::modular::AppConfig session_shell_config,
fuchsia::ui::views::ViewToken view_token);
@@ -224,6 +226,7 @@
fuchsia::modular::auth::AccountPtr account_;
std::unique_ptr<AppClient<fuchsia::modular::Lifecycle>> context_engine_app_;
+ std::unique_ptr<AppClient<fuchsia::modular::Lifecycle>> discovermgr_app_;
std::unique_ptr<AppClient<fuchsia::modular::Lifecycle>> module_resolver_app_;
std::unique_ptr<AppClient<fuchsia::modular::Lifecycle>> session_shell_app_;
std::unique_ptr<ViewHost> session_shell_view_host_;
@@ -262,6 +265,10 @@
component::ServiceProviderImpl module_resolver_ns_services_;
fuchsia::modular::ModuleResolverPtr module_resolver_service_;
+ // Services we provide to the discovermgr's namespace.
+ component::ServiceProviderImpl discovermgr_ns_services_;
+ fuchsia::app::discover::DiscoverRegistryPtr discover_registry_service_;
+
class PresentationProviderImpl;
std::unique_ptr<PresentationProviderImpl> presentation_provider_impl_;
diff --git a/peridot/bin/sessionmgr/story_runner/BUILD.gn b/peridot/bin/sessionmgr/story_runner/BUILD.gn
index a0a798e..c703444 100644
--- a/peridot/bin/sessionmgr/story_runner/BUILD.gn
+++ b/peridot/bin/sessionmgr/story_runner/BUILD.gn
@@ -69,6 +69,7 @@
"//peridot/public/lib/context/cpp:context_helper",
"//peridot/public/lib/entity/cpp:json",
"//peridot/public/lib/fostr/fidl/fuchsia.modular",
+ "//sdk/fidl/fuchsia.app.discover",
"//sdk/fidl/fuchsia.ledger",
"//sdk/fidl/fuchsia.modular",
"//sdk/fidl/fuchsia.modular.internal",
diff --git a/peridot/bin/sessionmgr/story_runner/module_context_impl.cc b/peridot/bin/sessionmgr/story_runner/module_context_impl.cc
index aab49c2..ea7256b 100644
--- a/peridot/bin/sessionmgr/story_runner/module_context_impl.cc
+++ b/peridot/bin/sessionmgr/story_runner/module_context_impl.cc
@@ -29,7 +29,8 @@
info.story_controller_impl->GetStoryId()),
EncodeModulePath(module_data_->module_path),
module_data_->module_url),
- user_intelligence_provider_(info.user_intelligence_provider) {
+ user_intelligence_provider_(info.user_intelligence_provider),
+ discover_registry_(info.discover_registry) {
service_provider_impl_.AddService<fuchsia::modular::ComponentContext>(
[this](
fidl::InterfaceRequest<fuchsia::modular::ComponentContext> request) {
@@ -52,6 +53,14 @@
user_intelligence_provider_->GetComponentIntelligenceServices(
std::move(*scope), std::move(request));
});
+ service_provider_impl_.AddService<fuchsia::app::discover::ModuleOutputWriter>(
+ [this](auto request) {
+ fuchsia::app::discover::ModuleIdentifier module_scope;
+ module_scope.set_story_id(story_controller_impl_->GetStoryId());
+ module_scope.set_module_path(module_data_->module_path);
+ discover_registry_->RegisterModuleOutputWriter(std::move(module_scope),
+ std::move(request));
+ });
service_provider_impl_.AddBinding(std::move(service_provider_request));
}
diff --git a/peridot/bin/sessionmgr/story_runner/module_context_impl.h b/peridot/bin/sessionmgr/story_runner/module_context_impl.h
index 00f2f9a..0abf2e6 100644
--- a/peridot/bin/sessionmgr/story_runner/module_context_impl.h
+++ b/peridot/bin/sessionmgr/story_runner/module_context_impl.h
@@ -5,6 +5,7 @@
#ifndef PERIDOT_BIN_SESSIONMGR_STORY_RUNNER_MODULE_CONTEXT_IMPL_H_
#define PERIDOT_BIN_SESSIONMGR_STORY_RUNNER_MODULE_CONTEXT_IMPL_H_
+#include <fuchsia/app/discover/cpp/fidl.h>
#include <fuchsia/modular/cpp/fidl.h>
#include <fuchsia/ui/views/cpp/fidl.h>
#include <lib/component/cpp/service_provider_impl.h>
@@ -30,6 +31,7 @@
StoryControllerImpl* const story_controller_impl;
StoryVisibilitySystem* const story_visibility_system;
fuchsia::modular::UserIntelligenceProvider* const user_intelligence_provider;
+ fuchsia::app::discover::DiscoverRegistry* const discover_registry;
};
// ModuleContextImpl keeps a single connection from a module instance in
@@ -131,6 +133,9 @@
fuchsia::modular::UserIntelligenceProvider* const
user_intelligence_provider_; // Not owned
+ fuchsia::app::discover::DiscoverRegistry* const
+ discover_registry_; // Not owned
+
fidl::BindingSet<fuchsia::modular::ModuleContext> bindings_;
// A service provider that represents the services to be added into an
diff --git a/peridot/bin/sessionmgr/story_runner/story_controller_impl.cc b/peridot/bin/sessionmgr/story_runner/story_controller_impl.cc
index b9fdbae..5c39d37 100644
--- a/peridot/bin/sessionmgr/story_runner/story_controller_impl.cc
+++ b/peridot/bin/sessionmgr/story_runner/story_controller_impl.cc
@@ -4,6 +4,7 @@
#include "peridot/bin/sessionmgr/story_runner/story_controller_impl.h"
+#include <fuchsia/app/discover/cpp/fidl.h>
#include <fuchsia/ledger/cpp/fidl.h>
#include <fuchsia/modular/cpp/fidl.h>
#include <fuchsia/modular/internal/cpp/fidl.h>
@@ -194,6 +195,8 @@
service_list->names.push_back(fuchsia::modular::ModuleContext::Name_);
service_list->names.push_back(
fuchsia::modular::IntelligenceServices::Name_);
+ service_list->names.push_back(
+ fuchsia::app::discover::ModuleOutputWriter::Name_);
service_list->provider = std::move(module_context_provider);
RunningModInfo running_mod_info;
@@ -235,7 +238,9 @@
story_controller_impl_,
story_controller_impl_->story_visibility_system_,
story_controller_impl_->story_provider_impl_
- ->user_intelligence_provider()};
+ ->user_intelligence_provider(),
+ story_controller_impl_->story_provider_impl_->discover_registry(),
+ };
running_mod_info.module_context_impl = std::make_unique<ModuleContextImpl>(
module_context_info, running_mod_info.module_data.get(),
diff --git a/peridot/bin/sessionmgr/story_runner/story_provider_impl.cc b/peridot/bin/sessionmgr/story_runner/story_provider_impl.cc
index 709bd7f..4207e35 100644
--- a/peridot/bin/sessionmgr/story_runner/story_provider_impl.cc
+++ b/peridot/bin/sessionmgr/story_runner/story_provider_impl.cc
@@ -298,6 +298,7 @@
fuchsia::modular::FocusProviderPtr focus_provider,
fuchsia::modular::UserIntelligenceProvider* const
user_intelligence_provider,
+ fuchsia::app::discover::DiscoverRegistry* const discover_registry,
fuchsia::modular::ModuleResolver* const module_resolver,
EntityProviderRunner* const entity_provider_runner,
modular::ModuleFacetReader* const module_facet_reader,
@@ -312,6 +313,7 @@
enable_story_shell_preload_(enable_story_shell_preload),
component_context_info_(component_context_info),
user_intelligence_provider_(user_intelligence_provider),
+ discover_registry_(discover_registry),
module_resolver_(module_resolver),
entity_provider_runner_(entity_provider_runner),
module_facet_reader_(module_facet_reader),
@@ -695,8 +697,7 @@
auto done = on_run->AsyncMap([this, story_id = info->focused_story_id] {
zx_time_t now = 0;
zx_clock_get_new(ZX_CLOCK_UTC, &now);
- return session_storage_->UpdateLastFocusedTimestamp(
- story_id, now);
+ return session_storage_->UpdateLastFocusedTimestamp(story_id, now);
});
fit::function<void()> callback = [] {};
operation_queue_.Add(WrapFutureAsOperation(
diff --git a/peridot/bin/sessionmgr/story_runner/story_provider_impl.h b/peridot/bin/sessionmgr/story_runner/story_provider_impl.h
index 45fd7b1..87d2cf2 100644
--- a/peridot/bin/sessionmgr/story_runner/story_provider_impl.h
+++ b/peridot/bin/sessionmgr/story_runner/story_provider_impl.h
@@ -5,6 +5,7 @@
#ifndef PERIDOT_BIN_SESSIONMGR_STORY_RUNNER_STORY_PROVIDER_IMPL_H_
#define PERIDOT_BIN_SESSIONMGR_STORY_RUNNER_STORY_PROVIDER_IMPL_H_
+#include <fuchsia/app/discover/cpp/fidl.h>
#include <fuchsia/ledger/cpp/fidl.h>
#include <fuchsia/modular/cpp/fidl.h>
#include <fuchsia/modular/internal/cpp/fidl.h>
@@ -59,6 +60,7 @@
const ComponentContextInfo& component_context_info,
fuchsia::modular::FocusProviderPtr focus_provider,
fuchsia::modular::UserIntelligenceProvider* user_intelligence_provider,
+ fuchsia::app::discover::DiscoverRegistry* discover_registry,
fuchsia::modular::ModuleResolver* module_resolver,
EntityProviderRunner* entity_provider_runner,
modular::ModuleFacetReader* module_facet_reader,
@@ -97,6 +99,11 @@
}
// Called by StoryControllerImpl.
+ fuchsia::app::discover::DiscoverRegistry* discover_registry() {
+ return discover_registry_;
+ }
+
+ // Called by StoryControllerImpl.
fuchsia::modular::ModuleResolver* module_resolver() {
return module_resolver_;
}
@@ -310,7 +317,9 @@
const ComponentContextInfo component_context_info_;
fuchsia::modular::UserIntelligenceProvider* const
- user_intelligence_provider_; // Not owned.
+ user_intelligence_provider_; // Not owned.
+ fuchsia::app::discover::DiscoverRegistry* const
+ discover_registry_; // Not owned.
fuchsia::modular::ModuleResolver* const module_resolver_; // Not owned.
EntityProviderRunner* const entity_provider_runner_; // Not owned.
modular::ModuleFacetReader* const module_facet_reader_; // Not owned.
diff --git a/peridot/tests/modular_tests.json b/peridot/tests/modular_tests.json
index d210eaa0..e4ce802 100644
--- a/peridot/tests/modular_tests.json
+++ b/peridot/tests/modular_tests.json
@@ -128,4 +128,4 @@
]
}
]
-}
\ No newline at end of file
+}
diff --git a/sdk/fidl/fuchsia.modular/entity/entity_resolver.fidl b/sdk/fidl/fuchsia.modular/entity/entity_resolver.fidl
index 2cbb35a..bcea5520 100644
--- a/sdk/fidl/fuchsia.modular/entity/entity_resolver.fidl
+++ b/sdk/fidl/fuchsia.modular/entity/entity_resolver.fidl
@@ -6,6 +6,7 @@
// This interface is given to all components in their |ComponentContext|. Any
// component can resolve an entity reference into an |Entity|.
+[Discoverable]
protocol EntityResolver {
// Finds and binds |entity_request| to an Entity handle for the Entity
// referenced by |entity_reference|. If an error occurs, |entity_request|
diff --git a/sdk/fidl/fuchsia.modular/fuchsia.modular.api b/sdk/fidl/fuchsia.modular/fuchsia.modular.api
index 4a3e03c..c29c790 100644
--- a/sdk/fidl/fuchsia.modular/fuchsia.modular.api
+++ b/sdk/fidl/fuchsia.modular/fuchsia.modular.api
@@ -19,7 +19,7 @@
"fidl/fuchsia.modular/entity/entity.fidl": "ca4659a784d2fd345a5348bcc0c78042",
"fidl/fuchsia.modular/entity/entity_provider.fidl": "8e3026210f5ad83d3c357603094877f0",
"fidl/fuchsia.modular/entity/entity_reference_factory.fidl": "5fc60e7fb24bd7878bd6203dc64bc915",
- "fidl/fuchsia.modular/entity/entity_resolver.fidl": "ab460a5e575d789311e63a54085c5930",
+ "fidl/fuchsia.modular/entity/entity_resolver.fidl": "9c1094116d0ef67d16d720069e41071b",
"fidl/fuchsia.modular/intent/intent.fidl": "de3880a06d64fa18dbbedcee7ac08053",
"fidl/fuchsia.modular/intent/intent_handler.fidl": "476ec09d958d7ba9b4934d9e4c255c90",
"fidl/fuchsia.modular/lifecycle/lifecycle.fidl": "1bc186061e6c737b060113bd203386b4",
diff --git a/src/modular/tests/BUILD.gn b/src/modular/tests/BUILD.gn
index 9ebb6f8..e6a5db9 100644
--- a/src/modular/tests/BUILD.gn
+++ b/src/modular/tests/BUILD.gn
@@ -78,6 +78,22 @@
]
}
+executable("module_output_test") {
+ testonly = true
+
+ sources = [
+ "module_output_test.cc",
+ ]
+
+ deps = [
+ "//peridot/public/lib/modular_test_harness/cpp:test_harness_fixture",
+ "//sdk/fidl/fuchsia.app.discover",
+ "//sdk/fidl/fuchsia.modular.testing",
+ "//sdk/lib/sys/cpp/testing:integration",
+ "//third_party/googletest:gtest_main",
+ ]
+}
+
executable("story_shell_factory_test") {
testonly = true
@@ -117,6 +133,10 @@
environments = basic_envs
},
{
+ name = "module_output_test"
+ environments = basic_envs
+ },
+ {
name = "story_shell_factory_test"
environments = basic_envs
},
@@ -125,6 +145,7 @@
deps = [
":intents_test",
":last_focus_time_test",
+ ":module_output_test",
":session_shell_test",
":story_shell_factory_test",
"//garnet/public/lib/callback",
diff --git a/src/modular/tests/meta/module_output_test.cmx b/src/modular/tests/meta/module_output_test.cmx
new file mode 100644
index 0000000..433f1ad
--- /dev/null
+++ b/src/modular/tests/meta/module_output_test.cmx
@@ -0,0 +1,13 @@
+{
+ "program": {
+ "binary": "test/module_output_test"
+ },
+ "sandbox": {
+ "services": [
+ "fuchsia.app.discover.ModuleOutput",
+ "fuchsia.sys.Launcher",
+ "fuchsia.sys.Environment",
+ "fuchsia.modular.ModuleContext"
+ ]
+ }
+}
diff --git a/src/modular/tests/module_output_test.cc b/src/modular/tests/module_output_test.cc
new file mode 100644
index 0000000..1491cee
--- /dev/null
+++ b/src/modular/tests/module_output_test.cc
@@ -0,0 +1,94 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fuchsia/app/discover/cpp/fidl.h>
+#include <fuchsia/modular/cpp/fidl.h>
+#include <fuchsia/modular/testing/cpp/fidl.h>
+#include <lib/modular_test_harness/cpp/fake_component.h>
+#include <lib/modular_test_harness/cpp/test_harness_fixture.h>
+#include <sdk/lib/sys/cpp/testing/test_with_environment.h>
+
+namespace {
+
+constexpr char kModuleName[] = "mod_name";
+constexpr char kStoryName[] = "story";
+constexpr char kIntentAction[] = "action";
+
+constexpr zx::duration kTimeout = zx::sec(15);
+
+class ModuleOutputTest : public modular::testing::TestHarnessFixture {
+ public:
+ void SetUp() override {
+ test_module_url_ = builder_.GenerateFakeUrl();
+ builder_.InterceptComponent(
+ component_.GetOnCreateHandler(),
+ {.url = test_module_url_,
+ .sandbox_services = {"fuchsia.app.discover.ModuleOutputWriter",
+ "fuchsia.modular.ModuleContext"}});
+
+ test_harness().events().OnNewComponent =
+ builder_.BuildOnNewComponentHandler();
+ test_harness()->Run(builder_.BuildSpec());
+ }
+
+ protected:
+ void CreateStoryAndAddMod(fuchsia::modular::Intent intent) {
+ // Create an AddMod command
+ fuchsia::modular::AddMod add_mod;
+ add_mod.mod_name = {kModuleName};
+ add_mod.intent = std::move(intent);
+ add_mod.surface_relation = fuchsia::modular::SurfaceRelation{};
+
+ fuchsia::modular::StoryCommand cmd;
+ cmd.set_add_mod(std::move(add_mod));
+
+ std::vector<fuchsia::modular::StoryCommand> cmds;
+ cmds.push_back(std::move(cmd));
+
+ // Connect to PuppetMaster Service
+ fuchsia::modular::PuppetMasterPtr puppet_master;
+ fuchsia::modular::testing::ModularService svc;
+ svc.set_puppet_master(puppet_master.NewRequest());
+ test_harness()->ConnectToModularService(std::move(svc));
+
+ // Create a story
+ fuchsia::modular::StoryPuppetMasterPtr story_master;
+ puppet_master->ControlStory(kStoryName, story_master.NewRequest());
+
+ // Add the initial module to the story
+ story_master->Enqueue(std::move(cmds));
+ story_master->Execute([&](fuchsia::modular::ExecuteResult result) {});
+ }
+
+ modular::testing::FakeComponent component_;
+ modular::testing::TestHarnessBuilder builder_;
+ std::string test_module_url_;
+};
+
+TEST_F(ModuleOutputTest, ModuleWritesToOutput) {
+ fuchsia::modular::Intent intent;
+ intent.handler = test_module_url_;
+ intent.action = kIntentAction;
+
+ CreateStoryAndAddMod(std::move(intent));
+ ASSERT_TRUE(RunLoopWithTimeoutOrUntil([&] { return component_.is_running(); },
+ kTimeout));
+
+ fuchsia::app::discover::ModuleOutputWriterPtr module_output;
+ component_.component_context()->svc()->Connect(module_output.NewRequest());
+ bool output_written{false};
+ module_output->Write("output_name", "reference",
+ [&output_written](auto result) {
+ // TODO: once the discover service generates
+ // suggestions, we should ensure they are generated
+ // based on this modules output.
+ ASSERT_TRUE(result.is_response());
+ output_written = true;
+ });
+
+ ASSERT_TRUE(RunLoopWithTimeoutOrUntil(
+ [&output_written] { return output_written; }, kTimeout));
+}
+
+} // namespace