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