lib45d
45Drives C++ Library Development Documentation
ConfigParser.hpp
1 // -*- C++ -*-
2 /*
3  * Copyright (C) 2021 Joshua Boudreau <jboudreau@45drives.com>
4  *
5  * This file is part of lib45d.
6  *
7  * lib45d is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * lib45d is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with lib45d. If not, see <https://www.gnu.org/licenses/>.
19  */
20 
21 #pragma once
22 
23 #include <45d/Quota.hpp>
24 #include <45d/config/ConfigNode.hpp>
25 #include <45d/config/Exceptions.hpp>
26 #include <sstream>
27 #include <string>
28 #include <unordered_map>
29 #include <vector>
30 
37 namespace ffd_internal {
48  template<class T>
49  T get(const std::string &key,
50  const std::unordered_map<std::string, ffd::ConfigNode> *config_map) {
51  ffd::ConfigNode node = config_map->at(key);
52  std::stringstream ss(node.value_);
53  ss.exceptions(std::ios::failbit | std::ios::badbit);
54  T result;
55  ss >> result;
56  return result;
57  }
58 } // namespace ffd_internal
59 
60 namespace ffd {
68  class ConfigParser {
73  friend class ConfigSubsectionGuard;
74  public:
82  ConfigParser(std::string path);
88  std::string dump_str(void) const;
104  template<class T>
105  T get(const std::string &key) const {
106  return ffd_internal::get<T>(key, config_map_ptr_);
107  }
119  template<class T>
120  T get(const std::string &key, const T &fallback) const noexcept {
121  try {
122  return get<T>(key);
123  } catch (const std::out_of_range &) {
124  // silently return fallback
125  } catch (const std::ios_base::failure &) {
126  try {
127  report_error("Invalid configuration entry format: " + key + " = "
128  + config_map_.at(key).value_);
129  } catch (const std::out_of_range &) {
130  report_error("Invalid configuration entry format for " + key);
131  }
132  } catch (const ConfigException &e) {
133  report_error(e.what());
134  } catch (const std::exception &e) {
135  report_error("Unexpected std::exception while getting " + key + ": " + e.what());
136  } catch (...) {
137  report_error("Unexplained exception caught while getting " + key);
138  }
139  return fallback;
140  }
154  template<class T>
155  T get(const std::string &key, bool *fail_flag) const noexcept {
156  try {
157  return get<T>(key);
158  } catch (const std::ios_base::failure &) {
159  try {
160  report_error("Invalid configuration entry format: " + key + " = "
161  + config_map_.at(key).value_);
162  } catch (const std::out_of_range &) {
163  report_error("Invalid configuration entry format for " + key);
164  }
165  } catch (const std::out_of_range &) {
166  report_error("Option not in config: " + key);
167  } catch (const ConfigException &e) {
168  report_error(e.what());
169  } catch (const std::exception &e) {
170  report_error("Unexpected std::exception while getting " + key + ": " + e.what());
171  } catch (...) {
172  report_error("Unexplained exception caught while getting " + key);
173  }
174  *fail_flag = true;
175  if (std::is_fundamental<T>::value)
176  return 0;
177  else
178  return T();
179  }
189  template<class T>
190  T get_from(const std::string &section, const std::string &key) {
191  if (guarded_)
193  "Cannot call get_from while ConfigSubsectionGuard is in scope"));
194  set_subsection(section);
195  T result = get<T>(key);
197  return result;
198  }
212  template<class T>
213  T get_from(const std::string &section, const std::string &key, const T &fallback) noexcept {
214  if (guarded_) {
215  std::cerr << "Cannot call get_from while ConfigSubsectionGuard is in scope"
216  << std::endl;
217  return fallback;
218  }
219  try {
220  set_subsection(section);
221  } catch (const std::out_of_range &) {
223  return fallback;
224  }
225  T result = get<T>(key, fallback);
227  return result;
228  }
239  template<class T>
240  T get_from(const std::string &section, const std::string &key, bool *fail_flag) noexcept {
241  if (guarded_) {
242  std::cerr << "Cannot call get_from while ConfigSubsectionGuard is in scope"
243  << std::endl;
244  *fail_flag = true;
245  if (std::is_fundamental<T>::value)
246  return 0;
247  else
248  return T();
249  }
250  try {
251  set_subsection(section);
252  } catch (const std::out_of_range &) {
253  std::cerr << "Section not in config: " << section << std::endl;
254  *fail_flag = true;
256  if (std::is_fundamental<T>::value)
257  return 0;
258  else
259  return T();
260  }
261  T result = get<T>(key, fail_flag);
263  return result;
264  }
281  Quota get_quota(const std::string &key, const Bytes &max) const {
282  return Quota(max, get<std::string>(key));
283  }
292  Quota
293  get_quota(const std::string &key, const Bytes &max, const Quota &fallback) const noexcept {
294  try {
295  return get_quota(key, max);
296  } catch (const std::out_of_range &) {
297  // silently return fallback
298  } catch (const std::ios_base::failure &) {
299  try {
300  report_error("Invalid configuration entry format: " + key + " = "
301  + config_map_.at(key).value_);
302  } catch (const std::out_of_range &) {
303  report_error("Invalid configuration entry format for " + key);
304  }
305  } catch (const ConfigException &e) {
306  report_error(e.what());
307  } catch (const std::exception &e) {
308  report_error("Unexpected std::exception while getting " + key + ": " + e.what());
309  } catch (...) {
310  report_error("Unexplained exception caught while getting " + key);
311  }
312  return fallback;
313  }
324  Quota get_quota(const std::string &key, const Bytes &max, bool *fail_flag) const noexcept {
325  try {
326  return get_quota(key, max);
327  } catch (const std::ios_base::failure &) {
328  try {
329  report_error("Invalid configuration entry format: " + key + " = "
330  + config_map_.at(key).value_);
331  } catch (const std::out_of_range &) {
332  report_error("Invalid configuration entry format for " + key);
333  }
334  } catch (const std::out_of_range &) {
335  report_error("Option not in config: " + key);
336  } catch (const ConfigException &e) {
337  report_error(e.what());
338  } catch (const std::exception &e) {
339  report_error("Unexpected std::exception while getting " + key + ": " + e.what());
340  } catch (...) {
341  report_error("Unexplained exception caught while getting " + key);
342  }
343  *fail_flag = true;
344  return Quota();
345  }
355  Quota get_quota_from(const std::string &section, const std::string &key, const Bytes &max) {
356  if (guarded_)
358  "Cannot call get_from while ConfigSubsectionGuard is in scope"));
359  set_subsection(section);
360  Quota result = get_quota(key, max);
362  return result;
363  }
377  Quota get_quota_from(const std::string &section,
378  const std::string &key,
379  const Bytes &max,
380  const Quota &fallback) noexcept {
381  if (guarded_) {
382  std::cerr << "Cannot call get_from while ConfigSubsectionGuard is in scope"
383  << std::endl;
384  return fallback;
385  }
386  try {
387  set_subsection(section);
388  } catch (const std::out_of_range &) {
390  return fallback;
391  }
392  Quota result = get_quota(key, max, fallback);
394  return result;
395  }
406  Quota get_quota_from(const std::string &section,
407  const std::string &key,
408  const Bytes &max,
409  bool *fail_flag) noexcept {
410  if (guarded_) {
411  std::cerr << "Cannot call get_from while ConfigSubsectionGuard is in scope"
412  << std::endl;
413  *fail_flag = true;
414  return Quota();
415  }
416  try {
417  set_subsection(section);
418  } catch (const std::out_of_range &) {
419  std::cerr << "Section not in config." << std::endl;
420  *fail_flag = true;
422  return Quota();
423  }
424  Quota result = get_quota(key, max, fail_flag);
426  return result;
427  }
428  protected:
429  std::vector<ConfigNode *> sub_confs_;
430  std::string current_section_;
431  private:
437  bool guarded_;
438  std::unordered_map<std::string, ConfigNode>
440  std::string path_;
441  std::unordered_map<std::string, ConfigNode>
443 
456  void parse(std::ifstream &file);
462  void parse_entry(const std::string &line);
473  void parse_heading(const std::string &line);
479  void set_subsection(const std::string &section) {
480  try {
481  config_map_ptr_ = config_map_.at(section).sub_map_;
482  } catch (const std::out_of_range &) {
483  throw std::out_of_range("No subconfig with name `" + section + "`");
484  }
485  if (config_map_ptr_ == nullptr)
486  throw std::out_of_range("`" + section + "` is not a subsection.");
487  current_section_ = section;
488  }
493  void reset_subsection(void) noexcept {
495  current_section_ = "";
496  }
503  void report_error(const std::string &message) const noexcept {
505  std::cerr << "[" << current_section_ << "]: ";
506  std::cerr << message << std::endl;
507  }
508  };
509 } // namespace ffd
ffd::ConfigParser::ConfigParser
ConfigParser(std::string path)
Construct a new Config Parser object.
Definition: config_parser.cpp:25
ffd::Bytes
Use this class for byte-formatted values. e.g.: "123 KiB".
Definition: Bytes.hpp:32
ffd::ConfigParser::get_from
T get_from(const std::string &section, const std::string &key)
Adapter for ffd::get(). Sets config_map_ptr_ to address of sub config with name section....
Definition: ConfigParser.hpp:190
ffd::ConfigParser::get_quota
Quota get_quota(const std::string &key, const Bytes &max, const Quota &fallback) const noexcept
Try to get Quota from config, default to fallback if fails. Guaranteed no-throw.
Definition: ConfigParser.hpp:293
ffd::Quota
This class extends ffd::Bytes to specify percents of an amount of bytes.
Definition: Quota.hpp:30
ffd::ConfigParser::get
T get(const std::string &key, bool *fail_flag) const noexcept
Try to get value from config. If ffd::get fails, return T() or 0 and set fail_flag....
Definition: ConfigParser.hpp:155
ffd::ConfigParser::dump_str
std::string dump_str(void) const
Dump config to stdout as a string.
Definition: config_parser.cpp:79
ffd::ConfigSubsectionGuard
Use this to switch to a certain config subsection to get a group of values.
Definition: ConfigSubsectionGuard.hpp:49
ffd_internal::get
T get(const std::string &key, const std::unordered_map< std::string, ffd::ConfigNode > *config_map)
Get config entry as type T from configuration map.
Definition: ConfigParser.hpp:49
ffd::ConfigParser::parse_entry
void parse_entry(const std::string &line)
Extract value from config line and insert ConfigNode into config_map_.
Definition: config_parser.cpp:57
ffd::ConfigParser::get_from
T get_from(const std::string &section, const std::string &key, bool *fail_flag) noexcept
Get value from config subsection using ConfigParser::get(const std::string&,bool*) const noexcept,...
Definition: ConfigParser.hpp:240
ffd::ConfigParser::get
T get(const std::string &key, const T &fallback) const noexcept
Try to get value from config, default to fallback if fails. Guaranteed no-throw.
Definition: ConfigParser.hpp:120
ffd::ConfigParser::parse
void parse(std::ifstream &file)
Iterate each line of config file and determine how to parse with l::check_record_type()
Definition: config_parser.cpp:37
ffd::ConfigParser::get
T get(const std::string &key) const
Get value from config map using ffd::get(). This can throw. Use this in a try...catch block.
Definition: ConfigParser.hpp:105
ffd
45Drives namespace
Definition: Bytes.hpp:27
ffd::ConfigParser::get_quota
Quota get_quota(const std::string &key, const Bytes &max, bool *fail_flag) const noexcept
Try to get Quota from config. If ffd::get fails, return Quota(void) and set fail_flag....
Definition: ConfigParser.hpp:324
ffd::ConfigParser::get_quota_from
Quota get_quota_from(const std::string &section, const std::string &key, const Bytes &max, bool *fail_flag) noexcept
Get value from config subsection using ConfigParser::get(const std::string&,bool*) const noexcept,...
Definition: ConfigParser.hpp:406
ffd::Exception::what
const char * what(void) const noexcept
Return string containing explanation message.
Definition: Exceptions.hpp:44
ffd::ConfigParser::get_quota
Quota get_quota(const std::string &key, const Bytes &max) const
Get quota from config map using ffd::get(). This can throw. Use this in a try...catch block.
Definition: ConfigParser.hpp:281
ffd::ConfigParser::config_map_
std::unordered_map< std::string, ConfigNode > config_map_
Map of config keys (std::string) to values (ConfigNode)
Definition: ConfigParser.hpp:442
ffd::ConfigParser::guarded_
bool guarded_
true if a ConfigSubsectionGuard is in scope Set in ConfigSubsectionGuard::ConfigSubsectionGuard() Cle...
Definition: ConfigParser.hpp:437
ffd::ConfigException
General exception for all Config* related issues.
Definition: Exceptions.hpp:30
ffd::ConfigGuardException
Throw this exception when a ConfigGuard is constructed or get_from() is called when the config is alr...
Definition: Exceptions.hpp:58
ffd::ConfigParser::current_section_
std::string current_section_
Name of current section, set by set_subsection()
Definition: ConfigParser.hpp:430
ffd::ConfigParser::set_subsection
void set_subsection(const std::string &section)
Update config_map_ptr_ to the subconfig map for section.
Definition: ConfigParser.hpp:479
ffd::ConfigParser::get_from
T get_from(const std::string &section, const std::string &key, const T &fallback) noexcept
Get value from config subsection using ConfigParser::get(const std::string&,const T&) const noexcept,...
Definition: ConfigParser.hpp:213
ffd::ConfigNode::value_
std::string value_
string from config file after '='
Definition: ConfigNode.hpp:33
ffd::ConfigParser::config_map_ptr_
std::unordered_map< std::string, ConfigNode > * config_map_ptr_
Pointer to current config map.
Definition: ConfigParser.hpp:439
ffd::ConfigParser::sub_confs_
std::vector< ConfigNode * > sub_confs_
Vector of config subsections.
Definition: ConfigParser.hpp:429
ffd::ConfigParser::get_quota_from
Quota get_quota_from(const std::string &section, const std::string &key, const Bytes &max, const Quota &fallback) noexcept
Get value from config subsection using ConfigParser::get(const std::string&,const T&) const noexcept,...
Definition: ConfigParser.hpp:377
ffd::ConfigParser::get_quota_from
Quota get_quota_from(const std::string &section, const std::string &key, const Bytes &max)
Adapter for ffd::get(). Sets config_map_ptr_ to address of sub config with name section....
Definition: ConfigParser.hpp:355
ffd::ConfigParser::reset_subsection
void reset_subsection(void) noexcept
Set config_map_ptr_ back to the address of config_map_.
Definition: ConfigParser.hpp:493
ffd::ConfigParser::report_error
void report_error(const std::string &message) const noexcept
Print error message to stderr, conditionally prepended with current subsection name.
Definition: ConfigParser.hpp:503
ffd::ConfigParser::parse_heading
void parse_heading(const std::string &line)
Create new subconfig.
Definition: config_parser.cpp:68
ffd::ConfigParser
Main configuration parser class to inherit from in your code.
Definition: ConfigParser.hpp:68
ffd::ConfigNode
Class for config_map_ entries.
Definition: ConfigNode.hpp:31
ffd_internal
Namespace for internal use, not to be exposed to ffd.
Definition: ConfigParser.hpp:37
ffd::ConfigParser::path_
std::string path_
Path to config file.
Definition: ConfigParser.hpp:440