1 #pragma once 2 3 #include "config.hpp" 4 #include "config_list.hpp" 5 6 #include <boost/format.hpp> 7 #include <boost/program_options.hpp> 8 9 namespace hocon { namespace program_options { 10 namespace po = boost::program_options; 11 12 template<class charT> 13 po::basic_parsed_options<charT> parse_hocon(shared_config cfg,const po::options_description & desc,bool allow_unregistered)14 parse_hocon(shared_config cfg, const po::options_description& desc, bool allow_unregistered) { 15 po::parsed_options result(&desc); 16 17 std::map<std::string, shared_config> to_do; 18 to_do.emplace("", cfg); 19 20 while (!to_do.empty()) { 21 auto iter = to_do.begin(); 22 auto prefix = iter->first; 23 auto cfg = iter->second; 24 to_do.erase(iter); 25 26 for (const auto& entry : cfg->entry_set()) { 27 po::option opt; 28 if (prefix.empty()) { 29 opt.string_key = entry.first; 30 } else { 31 opt.string_key = prefix + "." + entry.first; 32 } 33 34 // Skips options that are not registered in the description. 35 // This is different behavior than the built in program_options 36 // parsers, which pass the options along somehow. But for now 37 // it stops an exception from being thrown if there is an unknown 38 // item in the config file. 39 if (allow_unregistered && !desc.find_nothrow(opt.string_key, false)) { 40 continue; 41 } 42 43 if (entry.second->value_type() == config_value::type::LIST) { 44 // if this is a list, we want to check if any of the entries are 45 // objects. If so, we need to further expand those sub-trees, with 46 // list indices being expanded into our key 47 48 auto list = std::dynamic_pointer_cast<const config_list>(entry.second); 49 for (size_t i = 0; i < list->size(); ++i) { 50 const auto& value = list->get(i); 51 if (value->value_type() == config_value::type::LIST || 52 value->value_type() == config_value::type::OBJECT) { 53 boost::throw_exception(po::invalid_config_file_syntax(list->transform_to_string(), po::invalid_syntax::unrecognized_line)); 54 } else { 55 opt.value.push_back(value->transform_to_string()); 56 } 57 } 58 } else { 59 opt.value.push_back(entry.second->transform_to_string()); 60 } 61 if (!opt.value.empty()) { 62 result.options.emplace_back(std::move(opt)); 63 } 64 } 65 } 66 67 // let Boost convert our utf-8 entries to wchars if needed 68 return po::basic_parsed_options<charT>(result); 69 } 70 71 template<class charT> 72 po::basic_parsed_options<charT> parse_file(std::basic_string<charT> file_basename,const po::options_description & desc,bool allow_unregistered=false)73 parse_file(std::basic_string<charT> file_basename, const po::options_description& desc, bool allow_unregistered=false) { 74 shared_config cfg = config::parse_file_any_syntax(file_basename)->resolve(); 75 return parse_hocon<charT>(cfg, desc, allow_unregistered); 76 } 77 78 template<class charT> 79 po::basic_parsed_options<charT> parse_string(std::basic_string<charT> s,const po::options_description & desc,bool allow_unregistered=false)80 parse_string(std::basic_string<charT> s, const po::options_description& desc, bool allow_unregistered=false) { 81 shared_config cfg = config::parse_string(s)->resolve(); 82 return parse_hocon<charT>(cfg, desc, allow_unregistered); 83 } 84 }} 85