aboutsummaryrefslogtreecommitdiff
path: root/lib/configuration/configuration.cpp
blob: 34e84afe550aad2ce569d33eabb3cedd9ab15efe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/*
 * This file is part of smolbote. It's copyrighted by the contributors recorded
 * in the version control history of the file, available from its original
 * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote
 *
 * SPDX-License-Identifier: GPL-3.0
 */

#include "configuration.h"
#include <QStandardPaths>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <stdexcept>

static std::unique_ptr<Configuration> s_conf;

inline void strip(std::string &value)
{
    value.erase(value.begin(), std::find_if(value.begin(), value.end(), std::bind1st(std::not_equal_to<char>(), ' ')));
    value.erase(std::find_if(value.rbegin(), value.rend(), std::bind1st(std::not_equal_to<char>(), ' ')).base(), value.end());
}

Configuration::Configuration()
    : use_global(true)
{
    if(!s_conf)
        throw new std::runtime_error("Trying to use default Configuration, but none has been set!");
}

Configuration::Configuration(std::initializer_list<std::pair<std::string, conf_value_t>> l) noexcept
    : m_homePath(QStandardPaths::writableLocation(QStandardPaths::HomeLocation).toStdString())
{
    for(const auto &i : l) {
        insert_or_assign(i.first, i.second);
    }
}

void Configuration::read(std::basic_istream<char> &input)
{
    std::string line, key, value;
    std::istringstream is_line;

    while(std::getline(input, line)) {
        if(line[0] == '#' || line.length() == 0)
            continue;

        is_line.clear();
        is_line.str(line);

        if(std::getline(is_line, key, '=')) {
            is_line >> value;

            strip(key);
            strip(value);

            if(this->count(key) == 0) {
                // no type has been specified for this key, assuming std::string
                insert(std::make_pair(key, value));
                continue;
            }

            auto v = at(key);
            if(std::holds_alternative<std::string>(v)) {
                at(key) = value;
            } else if(std::holds_alternative<int>(v)) {
                at(key) = std::stoi(value);
            } else if(std::holds_alternative<bool>(v)) {
                at(key) = (value == "true");
            }
        }
    }
}

void Configuration::move_global(std::unique_ptr<Configuration> &&conf)
{
    s_conf = std::move(conf);
}

Configuration *Configuration::instance()
{
    return s_conf.get();
}

void setShortcut(QAction *action, const char *name)
{
    if(!s_conf)
        throw new std::runtime_error("Trying to set a shortcut, but no configuration has been set!");

    if(const auto shortcutText = s_conf->value<QString>(name)) {
        const QString tooltip = action->toolTip();
        action->setShortcut(QKeySequence::fromString(shortcutText.value()));
        action->setToolTip(QString("%1 (%2)").arg(tooltip, shortcutText.value()));
    }
#ifdef QT_DEBUG
    else {
        std::cout << "fixme: setShortcut called for " << name << ", but no such value exists!" << std::endl;
    }
#endif
}

std::ostream &operator<<(std::ostream &out, const Configuration &obj)
{
    // unordered_map is, well, unordered, so grab the keys and sort them before printing them
    std::vector<std::string> keys;

    if(obj.use_global) {
        if(!s_conf)
            throw new std::runtime_error("Trying to use default Configuration, but none has been set!");

        for(const auto &pair : *s_conf)
            keys.emplace_back(pair.first);
    } else {
        for(const auto &pair : obj)
            out << pair.first << "\n";
    }
    std::sort(keys.begin(), keys.end());

    for(const auto &key : keys) {
        if(obj.use_global)
            out << key << "=" << s_conf->value<std::string>(key.c_str()).value() << "\n";
        else
            out << key << "=" << obj.value<std::string>(key.c_str()).value() << "\n";
    }

    return out;
}