inicpp
C++ parser of INI files with schema validation.
section_schema.cpp
1 #include "section_schema.h"
2 #include "string_utils.h"
3 
4 namespace inicpp
5 {
7  : name_(source.name_), requirement_(source.requirement_), comment_(source.comment_)
8  {
9  // we have to do deep copies of option schemas
10  options_.reserve(source.options_.size());
11  for (auto &opt : source.options_) {
12  options_.push_back(std::make_shared<option_schema>(*opt));
13  }
14 
15  // we already have constructed option schemas... now push them into map
16  for (auto &opt : options_) {
17  options_map_.insert(opt_schema_map_pair(opt->get_name(), opt));
18  }
19  }
20 
22  {
23  if (this != &source) {
24  section_schema new_src(source);
25  std::swap(*this, new_src);
26  }
27 
28  return *this;
29  }
30 
32  {
33  *this = std::move(source);
34  }
35 
37  {
38  if (this != &source) {
39  name_ = std::move(source.name_);
40  requirement_ = std::move(source.requirement_);
41  comment_ = std::move(source.comment_);
42  options_ = std::move(source.options_);
43  options_map_ = std::move(source.options_map_);
44  }
45 
46  return *this;
47  }
48 
50  : name_(arguments.name), requirement_(arguments.requirement), comment_(arguments.comment)
51  {
52  }
53 
54  const std::string &section_schema::get_name() const
55  {
56  return name_;
57  }
58 
59  const std::string &section_schema::get_comment() const
60  {
61  return comment_;
62  }
63 
65  {
66  return (requirement_ == item_requirement::mandatory ? true : false);
67  }
68 
70  {
71  auto add_it = options_map_.find(opt.get_name());
72  if (add_it == options_map_.end()) {
73  std::shared_ptr<option_schema> add = std::make_shared<option_schema>(opt);
74  options_.push_back(add);
75  options_map_.insert(opt_schema_map_pair(add->get_name(), add));
76  } else {
77  throw ambiguity_exception(opt.get_name());
78  }
79  }
80 
81  void section_schema::remove_option(const std::string &option_name)
82  {
83  auto del_it = options_map_.find(option_name);
84  if (del_it != options_map_.end()) {
85  // remove from map
86  options_map_.erase(del_it);
87  // remove from vector
88  options_.erase(std::remove_if(options_.begin(),
89  options_.end(),
90  [&](std::shared_ptr<option_schema> opt) {
91  return (opt->get_name() == option_name ? true : false);
92  }),
93  options_.end());
94  } else {
95  throw not_found_exception(option_name);
96  }
97  }
98 
99  size_t section_schema::size() const
100  {
101  return options_.size();
102  }
103 
105  {
106  if (index >= size()) {
107  throw not_found_exception(index);
108  }
109 
110  return *options_[index];
111  }
112 
113  const option_schema &section_schema::operator[](size_t index) const
114  {
115  if (index >= size()) {
116  throw not_found_exception(index);
117  }
118 
119  return *options_[index];
120  }
121 
122  option_schema &section_schema::operator[](const std::string &option_name)
123  {
124  // its not pretty but the code is not copy pasted
125  // TODO: solve if it will be used or not
126  return const_cast<option_schema &>(static_cast<const section_schema *>(this)->operator[](option_name));
127  }
128 
129  const option_schema &section_schema::operator[](const std::string &option_name) const
130  {
131  std::shared_ptr<option_schema> result;
132  try {
133  result = options_map_.at(option_name);
134  } catch (std::out_of_range) {
135  throw not_found_exception(option_name);
136  }
137  return *result;
138  }
139 
140  bool section_schema::contains(const std::string &option_name) const
141  {
142  try {
143  options_map_.at(option_name);
144  return true;
145  } catch (std::out_of_range) {
146  return false;
147  }
148  }
149 
150  void section_schema::validate_section(section &sect, schema_mode mode) const
151  {
152  /*
153  * Here should be done:
154  * - check if section has proper options (compare by names) - depends on mode
155  * - for options with given schema call validate on that option
156  * - for missing options from schema (relaxed mode) add string options with
157  * default value
158  */
159 
160  // firstly go through option schemas
161  for (auto &opt : options_) {
162  bool contains = sect.contains(opt->get_name());
163 
164  if (contains) {
165  // even if option is not mandatory, we execute validation of option (both modes)
166  opt->validate_option(sect[opt->get_name()]);
167  } else if (opt->is_mandatory()) {
168  // mandatory option is not present in given section (both modes)
169  throw validation_exception(
170  "Mandatory option '" + opt->get_name() + "' is missing in section '" + sect.get_name() + "'");
171  } else {
172  // option is not mandatory and not in given section
173  // => add option with default value
174  sect.add_option(opt->get_name(), opt->get_default_value());
175  // validate added option, so type of the value could be changed to nonstring type
176  opt->validate_option(sect[opt->get_name()]);
177  }
178  }
179 
180  // secondly go through options
181  for (auto &opt : sect) {
182  bool contains = this->contains(opt.get_name());
183 
184  // if section_schema contains option everything is fine, we handled this above
185  if (contains) {
186  continue;
187  }
188 
189  // we have strict mode and option which is not in section_schema
190  if (mode == schema_mode::strict) {
191  throw validation_exception("Option '" + opt.get_name() + "' not specified in schema");
192  }
193  }
194  }
195 
196  std::ostream &section_schema::write_additional_info(std::ostream &os) const
197  {
198  // write comment
199  auto comment_lines = string_utils::split(get_comment(), '\n');
200  for (auto &comment_line : comment_lines) {
201  os << ";" << comment_line << std::endl;
202  }
203 
204  // optional/mandatory
205  std::string info_line = is_mandatory() ? "mandatory" : "optional";
206  os << ";<" << info_line << ">" << std::endl;
207 
208  return os;
209  }
210 
211  std::ostream &section_schema::write_section_name(std::ostream &os) const
212  {
213  os << "[" << get_name() << "]" << std::endl;
214  return os;
215  }
216 
217  std::ostream &operator<<(std::ostream &os, const section_schema &sect_schema)
218  {
219  // write additional information about given section
220  sect_schema.write_additional_info(os);
221 
222  // write name
223  sect_schema.write_section_name(os);
224 
225  // write all containing options
226  for (auto &opt : sect_schema.options_) {
227  os << *opt;
228  }
229 
230  return os;
231  }
232 }
bool contains(const std::string &option_name) const
Definition: section.cpp:128
void remove_option(const std::string &name)
std::ostream & write_additional_info(std::ostream &os) const
option_schema & operator[](size_t index)
const std::string & get_name() const
INICPP_API friend std::ostream & operator<<(std::ostream &os, const section_schema &sect_schema)
Definition: config.h:15
std::ostream & write_section_name(std::ostream &os) const
void add_option(const std::string &option_name, ValueType value)
Definition: section.h:89
const std::string & get_comment() const
void validate_section(section &sect, schema_mode mode) const
bool contains(const std::string &option_name) const
section_schema & operator=(const section_schema &source)
std::vector< std::string > split(const std::string &str, char delim)
bool is_mandatory() const
const std::string & get_name() const
void add_option(const option_schema &opt)
const std::string & get_name() const
Definition: section.cpp:49