JayBeams  0.1
Another project to have fun coding.
config_object.cpp
Go to the documentation of this file.
1 #include "jb/config_object.hpp"
2 
3 #include <jb/assert_throw.hpp>
5 #include <jb/log.hpp>
6 
7 #include <boost/program_options.hpp>
8 #include <fstream>
9 #include <iomanip>
10 
11 namespace po = boost::program_options;
12 
13 namespace {
14 bool config_file_found(
15  jb::config_files_locations<> const& search, std::string const& filename,
16  boost::filesystem::path& full_path) {
17  try {
18  full_path = search.find_configuration_file(filename);
19  return true;
20  } catch (std::runtime_error const&) {
21  // ... ignore search failures and return false ...
22  }
23  return false;
24 }
25 } // anonymous namespace
26 
28  : attributes_() {
29 }
30 
32  int& argc, char* argv[], std::string const& filename,
33  char const* environment_variable_name) {
34  char argv0[] = "undefined";
36  argc > 1 ? argv[0] : argv0, environment_variable_name);
37  boost::filesystem::path full;
38  if (config_file_found(search, filename, full)) {
39  JB_LOG(debug) << "loading overrides from " << full;
40  std::ifstream is(full.string());
41  load_overrides(argc, argv, is);
42  return;
43  }
44  process_cmdline(argc, argv);
45 }
46 
48  int& argc, char* argv[], std::string const& filename) {
49 
50  char argv0[] = "undefined";
51  jb::config_files_locations<> search(argc > 1 ? argv[0] : argv0);
52  boost::filesystem::path full;
53  if (config_file_found(search, filename, full)) {
54  JB_LOG(debug) << "loading overrides from " << full;
55  std::ifstream is(full.string());
56  load_overrides(argc, argv, is);
57  return;
58  }
59  process_cmdline(argc, argv);
60 }
61 
63  int& argc, char* argv[], std::istream& is) {
64  YAML::Node doc = YAML::Load(is);
65  apply_overrides(doc);
66  process_cmdline(argc, argv);
67 }
68 
69 void jb::config_object::apply_overrides(YAML::Node const& doc) {
70  class_overrides by_class;
71  jb::yaml::merge(by_class, doc);
72  apply_overrides(doc, by_class);
73 }
74 
76  YAML::Node const& by_name, class_overrides const& by_class) {
77  for (auto i : attributes_) {
78  if (i->class_name() != "") {
79  std::string key = std::string(":") + i->class_name();
80  auto n = by_class.find(key);
81  if (n != by_class.end()) {
82  i->apply_overrides(n->second, by_class);
83  }
84  }
85  class_overrides new_scope = jb::yaml::clone(by_class);
86  YAML::Node nested;
87  if (by_name and by_name.IsMap() and by_name[i->name()]) {
88  nested = by_name[i->name()];
89  }
90  jb::yaml::merge(new_scope, nested);
91  i->apply_overrides(nested, new_scope);
92  }
93 }
94 
95 void jb::config_object::process_cmdline(int& argc, char* argv[]) {
96  po::options_description options("Program Options");
97  options.add_options()("help", "produce help message")(
98  "help-in-test", "produce help message (Boost.Test captures --help)");
99 
100  add_options(
101  options, std::string(""), config_object::attribute_descriptor(""));
102 
103  // ... only top-level arguments get to be positional ...
104  po::positional_options_description positional;
105  for (auto i : attributes_) {
106  if (i->positional()) {
107  positional.add(i->name().c_str(), 1);
108  }
109  }
110 
111  po::variables_map vm;
112  po::store(
113  po::command_line_parser(argc, argv)
114  .options(options)
115  .positional(positional)
116  .run(),
117  vm);
118  po::notify(vm);
119 
120  if (vm.count("help") or vm.count("help-in-test")) {
121  std::ostringstream os;
122  os << options << "\n";
123  throw jb::usage{os.str(), 0};
124  }
125 
126  apply_cmdline_values(vm, std::string(""));
127  validate_all();
128 }
129 
131 }
132 
133 std::ostream& jb::config_object::to_stream(std::ostream& os) const {
134  YAML::Node doc = to_yaml();
135  return os << doc;
136 }
137 
139  po::options_description& options, std::string const& prefix,
140  attribute_descriptor const& d) const {
141  po::options_description group(d.helpmsg);
142 
143  for (auto i : attributes_) {
144  i->add_options(group, prefix, i->descriptor());
145  }
146  options.add(group);
147 }
148 
150  po::variables_map const& vm, std::string const& prefix) {
151  for (auto i : attributes_) {
152  i->apply_cmdline_values(vm, cmdline_arg_name(prefix, i->name()));
153  }
154 }
155 
157  attributes_.push_back(a);
158 }
159 
161  validate();
163 }
164 
166  for (auto i : attributes_) {
167  i->validate();
168  }
169 }
170 
172  std::string const& prefix, std::string const& name) {
173  if (prefix == "") {
174  return name;
175  }
176  std::string tmp = prefix;
177  tmp += ".";
178  tmp += name;
179  return tmp;
180 }
181 
182 YAML::Node jb::config_object::to_yaml() const {
183  YAML::Node doc;
184  for (auto const& i : attributes_) {
185  doc[i->name()] = i->to_yaml();
186  }
187  return doc;
188 }
189 
191  attribute_descriptor const& d, config_object* container)
192  : descriptor_(d) {
193  container->auto_register(this);
194 }
195 
197 }
attribute_base(attribute_descriptor const &d, config_object *container)
Constructor.
class_overrides clone(class_overrides const &by_class)
Recursively clone all the overrides in by_class.
Definition: merge_yaml.cpp:67
void merge(class_overrides &by_class, YAML::Node source)
Merge the class-overrides from source into by_class.
Definition: merge_yaml.cpp:40
Base class for all configuration objects.
virtual void validate() const
Validate the settings.
boost::filesystem::path find_configuration_file(std::string const &filename, validator_functor validator) const
Find a configuration file in the computed search path.
void validate_attributes() const
Run validate() on each attribute contained by this config_object.
std::map< std::string, YAML::Node > class_overrides
Store the overrides for each class.
Definition: merge_yaml.hpp:17
virtual ~attribute_base()=0
Destructor.
void load_overrides(int &argc, char *argv[], std::string const &filename, char const *environment_variable_name)
Read the configuration file and load the overrides defined therein.
config_object()
Default constructor.
std::vector< attribute_base * > attributes_
The list of attributes.
std::ostream & to_stream(std::ostream &os) const
Print out the settings.
void auto_register(attribute_base *a)
Define the interface to manipulate and access configuration attributes embedded in a config_object...
A simple class to communicate the result of parsing the options.
Definition: usage.hpp:11
void apply_cmdline_values(boost::program_options::variables_map const &vm, std::string const &prefix)
Apply the values from the cmdline to this configuration object.
void process_cmdline(int &argc, char *argv[])
Process the command line.
YAML::Node to_yaml() const
Print out the configuration settings in YAML format.
void apply_overrides(YAML::Node const &by_name)
Apply the overrides contained in the YAML document, compute the initial by_class overrides.
void validate_all() const
#define JB_LOG(lvl)
Definition: log.hpp:70
static std::string cmdline_arg_name(std::string const &prefix, std::string const &name)
Compute the full name of a command-line argument, given its prefix and short name.
void add_options(boost::program_options::options_description &options, std::string const &prefix, attribute_descriptor const &d) const
Add the attributes of this config_object as command-line options.
Compute the directories where a configuration file can be found.