| 195 | } |
| 196 | |
| 197 | void Options::LoadConfig(std::istream& stream, bool ignore_errors) { |
| 198 | json::UnknownElement config_root; |
| 199 | |
| 200 | try { |
| 201 | json::Reader::Read(config_root, stream); |
| 202 | } catch (json::Reader::ParseException& e) { |
| 203 | LOG_E("option/load") << "json::ParseException: " << e.what() << ", Line/offset: " << e.m_locTokenBegin.m_nLine + 1 << '/' << e.m_locTokenBegin.m_nLineOffset + 1; |
| 204 | return; |
| 205 | } catch (json::Exception& e) { |
| 206 | LOG_E("option/load") << "json::Exception: " << e.what(); |
| 207 | return; |
| 208 | } |
| 209 | |
| 210 | ConfigVisitor config_visitor(ignore_errors); |
| 211 | config_root.Accept(config_visitor); |
| 212 | |
| 213 | auto new_values = config_visitor.Values(); |
| 214 | if (new_values.empty()) return; |
| 215 | |
| 216 | sort(begin(new_values), end(new_values), option_name_cmp()); |
| 217 | |
| 218 | if (values.empty()) { |
| 219 | values = std::move(new_values); |
| 220 | return; |
| 221 | } |
| 222 | |
| 223 | auto src_it = begin(new_values), src_end = end(new_values); |
| 224 | auto dst_it = begin(values), dst_end = end(values); |
| 225 | |
| 226 | while (src_it != src_end && dst_it != dst_end) { |
| 227 | int cmp = (*src_it)->GetName().compare((*dst_it)->GetName()); |
| 228 | if (cmp < 0) // Option doesn't exist in defaults so skip |
| 229 | ++src_it; |
| 230 | else if (cmp > 0) |
| 231 | ++dst_it; |
| 232 | else { |
| 233 | if (ignore_errors) { |
| 234 | try { |
| 235 | (*dst_it)->Set((*src_it).get()); |
| 236 | } |
| 237 | catch (agi::InternalError const& e) { |
| 238 | LOG_E("option/load/config_visitor") << "Error loading option from user configuration: " << e.GetMessage(); |
| 239 | } |
| 240 | } |
| 241 | else { |
| 242 | *dst_it = std::move(*src_it); |
| 243 | } |
| 244 | ++src_it; |
| 245 | ++dst_it; |
| 246 | } |
| 247 | } |
| 248 | } |
| 249 | |
| 250 | OptionValue *Options::Get(std::string_view name) { |
| 251 | auto index = lower_bound(begin(values), end(values), name, option_name_cmp()); |