.. _program_listing_file_include_domain_bridge_process_cmd_line_arguments.hpp: Program Listing for File process_cmd_line_arguments.hpp ======================================================= |exhale_lsh| :ref:`Return to documentation for file ` (``include/domain_bridge/process_cmd_line_arguments.hpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp // 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 #include #include #include #include #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 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 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, int> process_cmd_line_arguments(const std::vector & 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 from_domain_id; std::optional to_domain_id; std::optional wait_for_subscription; std::optional mode; std::vector yaml_configs; std::optional wait_for_publisher; std::optional 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_