Program Listing for File process_cmd_line_arguments.hpp
↰ Return to documentation for file (include/domain_bridge/process_cmd_line_arguments.hpp
)
// Copyright 2021, Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef DOMAIN_BRIDGE__PROCESS_CMD_LINE_ARGUMENTS_HPP_
#define DOMAIN_BRIDGE__PROCESS_CMD_LINE_ARGUMENTS_HPP_
#include <filesystem>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "domain_bridge/domain_bridge_config.hpp"
namespace domain_bridge
{
namespace detail
{
static constexpr char kCompressModeStr[] = "compress";
static constexpr char kDecompressModeStr[] = "decompress";
static constexpr char kNormalModeStr[] = "normal";
inline
void
print_help()
{
std::cerr << "Usage: domain_bridge "
"[--from FROM_DOMAIN_ID] [--to TO_DOMAIN_ID] [-h] YAML_CONFIG ..." << std::endl <<
std::endl <<
"Arguments:" << std::endl <<
" YAML_CONFIG path to a YAML configuration file." << std::endl <<
std::endl <<
"Options:" << std::endl <<
" --from FROM_DOMAIN_ID All data will be bridged from this domain ID. " << std::endl <<
" This overrides any domain IDs set in the YAML file." <<
std::endl <<
" --to TO_DOMAIN_ID All data will be bridged to this domain ID. " << std::endl <<
" This overrides any domain IDs set in the YAML file." <<
std::endl <<
" --mode MODE_STR Specify the bridge mode, valid values: '" << kCompressModeStr <<
"', '" << kDecompressModeStr << "' or '" << kNormalModeStr << "'. " << std::endl <<
" This overrides any mode set in the YAML file." << std::endl <<
" Defaults to '" << kNormalModeStr << "'." <<
std::endl <<
" --wait-for-subscription true|false Will wait for an available subscription before"
" bridging a topic. This overrides any value set in the YAML file. Defaults to false." <<
std::endl <<
" --wait-for-publisher true|false Will wait for an available subscription before"
" bridging a topic. This overrides any value set in the YAML file. Defaults to true." <<
std::endl <<
" --auto-remove true|false If true, the bridge will be removed if the endpoint that was"
" waited on is removed. I.e. if both --wait-for-subscription and --wait-for-publisher were"
" passed, then when either the subscription or publisher is removed, the bridge will also be"
" removed."
" If only --wait-for-subscription was passed, the bridge will only be removed if the"
" subscription is."
" The bridge will be recreated when the original \"wait for\" condition is satisfied." <<
std::endl <<
" --help, -h Print this help message." << std::endl;
}
inline
std::optional<size_t>
parse_size_t_arg(const std::string & arg, const char * error_str)
{
size_t value;
std::istringstream iss(arg);
iss >> value;
if (iss.fail() || !iss.eof()) {
std::cerr << "error: Failed to parse " << error_str << " '" <<
arg << "'" << std::endl;
return std::nullopt;
}
return value;
}
inline
std::optional<bool>
parse_bool_arg(const std::string & arg, const char * error_str)
{
if (arg.empty()) {
std::cerr << "error: Failed to parse " << error_str << " argument. " <<
"Must be followed by true|false" << std::endl;
return std::nullopt;
}
bool value;
std::istringstream iss(arg);
iss >> std::boolalpha >> value;
if (iss.fail()) {
std::cerr << "error: Failed to parse " << error_str << " argument '" <<
arg << "'" << std::endl;
return std::nullopt;
}
return value;
}
} // namespace detail
inline
std::pair<std::optional<DomainBridgeConfig>, int>
process_cmd_line_arguments(const std::vector<std::string> & args)
{
if (args.size() < 2) {
std::cerr << "error: Expected YAML config file" << std::endl;
detail::print_help();
return std::make_pair(std::nullopt, 1);
}
std::optional<size_t> from_domain_id;
std::optional<size_t> to_domain_id;
std::optional<bool> wait_for_subscription;
std::optional<domain_bridge::DomainBridgeOptions::Mode> mode;
std::vector<std::filesystem::path> yaml_configs;
std::optional<bool> wait_for_publisher;
std::optional<bool> auto_remove;
for (auto it = ++args.cbegin() /*skip executable name*/; it != args.cend(); ++it) {
const auto & arg = *it;
if (arg == "-h" || arg == "--help") {
detail::print_help();
return std::make_pair(std::nullopt, 0);
}
if (arg == "--from") {
if (from_domain_id) {
std::cerr << "error: --from option passed more than once" << std::endl;
detail::print_help();
return std::make_pair(std::nullopt, 1);
}
++it;
if (it == args.cend()) {
std::cerr << "--from must be followed by a domain id" << std::endl;
detail::print_help();
return std::make_pair(std::nullopt, 1);
}
from_domain_id = detail::parse_size_t_arg(*it, "FROM_DOMAIN_ID");
if (!from_domain_id) {
return std::make_pair(std::nullopt, 1);
}
continue;
}
if (arg == "--to") {
if (to_domain_id) {
std::cerr << "error: --to option passed more than once" << std::endl;
detail::print_help();
return std::make_pair(std::nullopt, 1);
}
++it;
if (it == args.cend()) {
std::cerr << "--to must be followed by a domain id" << std::endl;
detail::print_help();
return std::make_pair(std::nullopt, 1);
}
to_domain_id = detail::parse_size_t_arg(*it, "TO_DOMAIN_ID");
if (!to_domain_id) {
return std::make_pair(std::nullopt, 1);
}
continue;
}
if (arg == "--wait-for-subscription") {
if (wait_for_subscription) {
std::cerr << "error: --wait-for-subscription option passed more than once" << std::endl;
detail::print_help();
return std::make_pair(std::nullopt, 1);
}
++it;
if (it == args.cend()) {
std::cerr << "--wait-for-subscription must be followed by true|false" << std::endl;
detail::print_help();
return std::make_pair(std::nullopt, 1);
}
wait_for_subscription = detail::parse_bool_arg(*it, "--wait-for-subscription");
if (!wait_for_subscription) {
return std::make_pair(std::nullopt, 1);
}
continue;
}
if (arg == "--wait-for-publisher") {
if (wait_for_subscription) {
std::cerr << "error: --wait-for-publisher option passed more than once" << std::endl;
detail::print_help();
return std::make_pair(std::nullopt, 1);
}
++it;
if (it == args.cend()) {
std::cerr << "--wait-for-publisher must be followed by true|false" << std::endl;
detail::print_help();
return std::make_pair(std::nullopt, 1);
}
wait_for_publisher = detail::parse_bool_arg(*it, "--wait-for-publisher");
if (!wait_for_publisher) {
return std::make_pair(std::nullopt, 1);
}
continue;
}
if (arg == "--mode") {
if (mode) {
std::cerr << "error: --mode option passed more than once" << std::endl;
detail::print_help();
return std::make_pair(std::nullopt, 1);
}
++it;
if (it == args.cend()) {
std::cerr << "--mode must be followed by compress|decompress|normal" << std::endl;
detail::print_help();
return std::make_pair(std::nullopt, 1);
}
const auto & mode_str = *it;
if (mode_str == detail::kCompressModeStr) {
mode = domain_bridge::DomainBridgeOptions::Mode::Compress;
} else if (mode_str == detail::kDecompressModeStr) {
mode = domain_bridge::DomainBridgeOptions::Mode::Decompress;
} else if (mode_str == detail::kNormalModeStr) {
mode = domain_bridge::DomainBridgeOptions::Mode::Normal;
} else {
std::cerr << "error: Invalid '--mode' option '" <<
mode_str << "'" << std::endl;
return std::make_pair(std::nullopt, 1);
}
continue;
}
if (arg == "--auto-remove") {
if (auto_remove) {
std::cerr << "error: --auto-remove option passed more than once" << std::endl;
detail::print_help();
return std::make_pair(std::nullopt, 1);
}
++it;
if (it == args.cend()) {
std::cerr << "--auto-remove must be followed by true|false" << std::endl;
detail::print_help();
return std::make_pair(std::nullopt, 1);
}
auto_remove = detail::parse_bool_arg(*it, "--auto-remove");
if (!auto_remove) {
return std::make_pair(std::nullopt, 1);
}
continue;
}
yaml_configs.push_back(std::filesystem::path{*it});
}
if (yaml_configs.empty()) {
std::cerr << "error: Must specify at least one yaml configuration file" << std::endl;
return std::make_pair(std::nullopt, 1);
}
domain_bridge::DomainBridgeConfig domain_bridge_config =
domain_bridge::parse_domain_bridge_yaml_configs(yaml_configs);
// Override domain bridge configuration options
if (
from_domain_id || to_domain_id || wait_for_subscription || wait_for_publisher ||
auto_remove)
{
for (auto & topic_option_pair : domain_bridge_config.topics) {
if (from_domain_id) {
topic_option_pair.first.from_domain_id = *from_domain_id;
}
if (to_domain_id) {
topic_option_pair.first.to_domain_id = *to_domain_id;
}
if (wait_for_subscription) {
topic_option_pair.second.wait_for_subscription(*wait_for_subscription);
}
if (wait_for_publisher) {
topic_option_pair.second.wait_for_publisher(*wait_for_publisher);
}
if (auto_remove) {
auto auto_remove_enum = TopicBridgeOptions::AutoRemove::Disabled;
if (*auto_remove) {
bool actual_wait_for_publisher = topic_option_pair.second.wait_for_publisher();
bool actual_wait_for_subscription = topic_option_pair.second.wait_for_subscription();
if (actual_wait_for_publisher && actual_wait_for_subscription) {
auto_remove_enum = TopicBridgeOptions::AutoRemove::OnNoPublisherOrSubscription;
} else if (actual_wait_for_publisher) {
auto_remove_enum = TopicBridgeOptions::AutoRemove::OnNoPublisher;
} else if (actual_wait_for_subscription) {
auto_remove_enum = TopicBridgeOptions::AutoRemove::OnNoSubscription;
}
}
topic_option_pair.second.auto_remove(auto_remove_enum);
}
}
}
if (mode) {
domain_bridge_config.options.mode(*mode);
}
return std::make_pair(domain_bridge_config, 0);
}
} // namespace domain_bridge
#endif // DOMAIN_BRIDGE__PROCESS_CMD_LINE_ARGUMENTS_HPP_