1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "libconfig/libconfig.h"
4
5#include <fstream>
6#include <iostream>
7
8std::optional<Config> Config::from_file(const std::string &filepath)
9{
10 std::fstream f(filepath, f.in);
11 if (!f)
12 return std::nullopt;
13
14 Config config;
15
16 Config::SectionType current_section = std::make_pair(x: "global", y: Config::SectionContentType{});
17 for (std::string line; std::getline(is&: f, str&: line);)
18 {
19 if (line.empty())
20 continue;
21
22 if (line.starts_with(x: '#'))
23 continue;
24
25 if (line.starts_with(x: '[') && line.ends_with(x: ']'))
26 {
27 // a new section
28 config.sections.push_back(x: current_section);
29 current_section = std::make_pair(x: line.substr(pos: 1, n: line.size() - 2), y: Config::SectionContentType{}); // remove the brackets
30 }
31 else
32 {
33 // a key-value pair
34 size_t pos = line.find(c: '=');
35 if (pos == std::string::npos)
36 {
37 std::cerr << "Invalid line: " << line << std::endl;
38 continue;
39 }
40
41 std::string key = line.substr(pos: 0, n: pos);
42 std::string value = line.substr(pos: pos + 1);
43
44 const auto remove_leading_trailing_spaces = [](std::string &str)
45 {
46 while (!str.empty() && str.front() == ' ')
47 str.erase(pos: 0, n: 1);
48 while (!str.empty() && str.back() == ' ')
49 str.pop_back();
50 };
51
52 remove_leading_trailing_spaces(key);
53 remove_leading_trailing_spaces(value);
54
55 current_section.second.push_back(x: { key, value });
56 }
57 }
58
59 config.sections.push_back(x: current_section);
60
61 return config;
62}
63
64std::optional<Config::SectionType> Config::get_section(const std::string &section_name) const
65{
66 for (const auto &section : sections)
67 if (section.first == section_name)
68 return section;
69
70 return std::nullopt;
71}
72
73std::vector<Config::EntryType> Config::get_entry(const std::string &section_name, const std::string &key) const
74{
75 auto entries = get_section(section_name);
76 if (!entries)
77 return {};
78 return entries->second | std::ranges::views::filter([&key](const auto &entry) { return entry.first == key; }) | std::ranges::to<std::vector<EntryType>>();
79}
80
81std::vector<Config::EntryType> Config::get_entries(const std::string &section_name) const
82{
83 auto entries = get_section(section_name);
84 if (!entries)
85 return {};
86 return entries->second;
87}
88