Browse Source

Add configuration subcommand

--dump: Write current configuration to stdout and exit
master
Aqua-sama 1 month ago
parent
commit
eb31183853
Signed by: Aqua-sama <aqua@iserlohn-fortress.net> GPG Key ID: 81A99984EABC02D1
6 changed files with 139 additions and 68 deletions
  1. +34
    -10
      lib/configuration/configuration.cpp
  2. +29
    -22
      lib/configuration/configuration.h
  3. +2
    -1
      meson.build
  4. +30
    -11
      src/builtins.cpp
  5. +6
    -1
      src/builtins.h
  6. +38
    -23
      src/main.cpp

+ 34
- 10
lib/configuration/configuration.cpp View File

@@ -7,11 +7,11 @@
*/

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

static std::unique_ptr<Configuration> s_conf;

@@ -21,7 +21,6 @@ inline void strip(std::string &value)
value.erase(std::find_if(value.rbegin(), value.rend(), std::bind1st(std::not_equal_to<char>(), ' ')).base(), value.end());
}


Configuration::Configuration()
: use_global(true)
{
@@ -32,9 +31,9 @@ Configuration::Configuration()
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);
}
for(const auto &i : l) {
insert_or_assign(i.first, i.second);
}
}

void Configuration::read(std::basic_istream<char> &input)
@@ -69,18 +68,16 @@ void Configuration::read(std::basic_istream<char> &input)
} 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()
Configuration *Configuration::instance()
{
return s_conf.get();
}
@@ -101,3 +98,30 @@ void setShortcut(QAction *action, const char *name)
}
#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;
}

+ 29
- 22
lib/configuration/configuration.h View File

@@ -9,21 +9,21 @@
#ifndef SMOLBOTE_CONFIGURATION_H
#define SMOLBOTE_CONFIGURATION_H

#include <QAction>
#include <QString>
#include <initializer_list>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include <unordered_map>
#include <variant>
#include <initializer_list>
#include <memory>
#include <QAction>
#include <QString>
#include <vector>

#if defined (__clang__)
#define consumable(X) [[clang::consumable(X)]]
#define return_typestate(X) [[clang::return_typestate(X)]]
#define callable_when(X) [[clang::callable_when(X)]]
#define param_typestate(X) [[clang::param_typestate(X)]]
#if defined(__clang__)
#define consumable(X) [[clang::consumable(X)]]
#define return_typestate(X) [[clang::return_typestate(X)]]
#define callable_when(X) [[clang::callable_when(X)]]
#define param_typestate(X) [[clang::param_typestate(X)]]
#else
#define consumable(X)
#define return_typestate(X)
@@ -35,27 +35,25 @@ typedef std::variant<std::string, int, bool> conf_value_t;

class consumable(unconsumed) Configuration : private std::unordered_map<std::string, conf_value_t>
{
friend std::ostream &operator<<(std::ostream &out, const Configuration &obj);

public:
return_typestate(unconsumed)
explicit Configuration();
return_typestate(unconsumed) explicit Configuration();

return_typestate(unconsumed)
explicit Configuration(std::initializer_list<std::pair<std::string, conf_value_t>> l) noexcept;
return_typestate(unconsumed) explicit Configuration(std::initializer_list<std::pair<std::string, conf_value_t>> l) noexcept;

explicit Configuration(Configuration &&other param_typestate(unconsumed)) = default;
explicit Configuration(Configuration && other param_typestate(unconsumed)) = default;

~Configuration() = default;

callable_when(unconsumed)
void read(std::basic_istream<char> &input);
callable_when(unconsumed) void read(std::basic_istream<char> & input);

template <typename T>
callable_when(unconsumed) std::optional<T> value(const char *path) const
{
if(use_global)
return instance()->value<T>(path);
if(this->count(path) == 0) {
return std::nullopt;
}
@@ -64,7 +62,15 @@ public:
const auto value = at(path);

if constexpr(std::is_same_v<T, QString> || std::is_same_v<T, std::string>) {
auto r = std::get<std::string>(value);
auto r = [&value]() {
if(std::holds_alternative<std::string>(value))
return std::get<std::string>(value);
else if(std::holds_alternative<int>(value))
return std::to_string(std::get<int>(value));
else
return std::get<bool>(value) ? std::string("true") : std::string("false");
}();

if(r.front() == '~')
r.replace(0, 1, m_homePath);

@@ -76,14 +82,14 @@ public:
} else if constexpr(std::is_same_v<T, QStringList>) {
return std::make_optional(QString::fromStdString(std::get<std::string>(value)).split(';'));

} else if (std::holds_alternative<T>(value)) {
} else if(std::holds_alternative<T>(value)) {
return std::optional<T>(std::get<T>(value));
} else
return std::nullopt;
} // std::optional<T> value(path) const

static void move_global(std::unique_ptr<Configuration> &&conf);
static void move_global(std::unique_ptr<Configuration> && conf);

private:
static Configuration *instance();
@@ -93,5 +99,6 @@ private:
};

void setShortcut(QAction *action, const char *name);
std::ostream &operator<<(std::ostream &out, const Configuration &obj);

#endif // SMOLBOTE_CONFIGURATION_H

+ 2
- 1
meson.build View File

@@ -48,7 +48,8 @@ add_project_arguments(cxx.get_supported_arguments([

'-mspeculative-load-hardening', # Spectre v1 mitigation

'-Wconsumed',
'-Wconsumed', # (clang) use-after-move warnings
'-Wdate-time', # Warn when using __TIME__ and __DATE__ macros
'-Wimplicit-fallthrough',
]), language: 'cpp')


+ 30
- 11
src/builtins.cpp View File

@@ -7,20 +7,11 @@
*/

#include "builtins.h"
#include "configuration.h"
#include "version.h"
#include <QObject>
#include <QVersionNumber>
#include <iostream>
#include "version.h"
#include <QTranslator>

inline const char* tr(const QTranslator *translator, const char *text)
{
const auto t = translator->translate("builtins", text);
if(t.isEmpty())
return text;
else
return qUtf8Printable(t);
}

int builtins::version()
{
@@ -34,3 +25,31 @@ int builtins::build()
std::cout << poi_Version << std::endl;
return 0;
}

int builtins::configuration(const std::string &progname, std::vector<std::string>::const_iterator beginargs, std::vector<std::string>::const_iterator endargs)
{
args::ArgumentParser parser("configuration");
parser.Prog(progname);

args::HelpFlag help(parser, "help", "Display this help message and exit.", { 'h', "help" });
args::Flag dump(parser, "dump", "Dump currently used configuration and exit", { "dump" });

try {
parser.ParseArgs(beginargs, endargs);
} catch(args::Help &e) {
std::cout << parser;
return 0;
} catch(args::Error &e) {
std::cerr << e.what() << std::endl;
std::cerr << parser;
return -1;
}

if(dump) {
Configuration conf;
std::cout << conf << std::endl;
return 0;
}

return 0;
}

+ 6
- 1
src/builtins.h View File

@@ -9,13 +9,18 @@
#ifndef SMOLBOTE_BUILTINS_H
#define SMOLBOTE_BUILTINS_H

#include <boost/program_options.hpp>
#include <args.hxx>
#include <plugininterface.h>

typedef std::function<int(const std::string &, std::vector<std::string>::const_iterator, std::vector<std::string>::const_iterator)> subcommand_func;
typedef std::unordered_map<std::string, subcommand_func> command_map;

namespace builtins
{
int version();
int build();

int configuration(const std::string &progname, std::vector<std::string>::const_iterator beginargs, std::vector<std::string>::const_iterator endargs);
}

#endif

+ 38
- 23
src/main.cpp View File

@@ -18,16 +18,12 @@
#include <QFile>
#include <QPluginLoader>
#include <QStandardPaths>
#include <args.hxx>
#include <iostream>
#include <memory>
#include <plugininterface.h>
#include <pluginloader.h>
#include <spdlog/spdlog.h>

typedef std::function<void(const std::string &, std::vector<std::string>::const_iterator, std::vector<std::string>::const_iterator)> subcommand_func;
typedef std::unordered_map<std::string, subcommand_func> command_map;

// a helper function to join the keys of a command_map into a string
inline std::string join_keys(const command_map &map, const std::string sep = ", ")
{
@@ -59,9 +55,19 @@ int main(int argc, char **argv)
#endif

const std::vector<std::string> args(argv + 1, argv + argc);
args::ArgumentParser parser("smolbote: yet another no-frills browser", "For more information on usage, refer to the manual page.");
args::ArgumentParser parser("smolbote: yet another no-frills browser",
#ifdef Q_OS_UNIX
"For more information on usage, refer to the manual page."
#else
"For more information on usage, refer to the manual."
#endif
);
parser.Prog(argv[0]);

const command_map commands{
{ "configuration", builtins::configuration }
};

args::HelpFlag cmd_help(parser, "help", "Display this help message.", { 'h', "help" });
args::Flag cmd_version(parser, "version", "Display version information.", { 'v', "version" });
args::Flag cmd_build(parser, "build", "Display build commit.", { 'b', "build" });
@@ -74,19 +80,44 @@ int main(int argc, char **argv)
args::ValueFlag<std::string> cmd_session(parser, "session", "Open the specified session.", { 's', "session" });

args::PositionalList<std::string> cmd_args(parser, "URL(s)", "List of URLs to open");
cmd_args.KickOut(true);

try {
/*auto next = */ parser.ParseArgs(args);
auto next = parser.ParseArgs(args);

if(cmd_version)
return builtins::version();
if(cmd_build)
return builtins::build();

// create and load configuration
const std::string config_path = [&]() {
std::string path;
if(cmd_config)
path = args::get(cmd_config);
else
path = std::string(CONFIG_POI_CFG_PATH);

if(path.front() == '~')
path.replace(0, 1, QStandardPaths::writableLocation(QStandardPaths::HomeLocation).toStdString());

return path;
}();
spdlog::debug("Opening config file {}", config_path);
init_conf(config_path);

if(cmd_args) {
const auto front = args::get(cmd_args).front();
const auto cmd = commands.find(front);
if(cmd != commands.end()) {
return cmd->second(argv[0], next, std::end(args));
}
}

} catch(args::Help &e) {
std::cout << parser;
Q_UNUSED(e);
//std::cout << "Available subcommands: " << command_keys << std::endl;
std::cout << "Available subcommands: " << join_keys(commands) << std::endl;
return 0;

} catch(args::Error &e) {
@@ -102,22 +133,6 @@ int main(int argc, char **argv)
qputenv("QTWEBENGINE_CHROMIUM_FLAGS", chromiumFlags + " --disable-in-process-stack-traces");
}

// create and load configuration
const std::string config_path = [&]() {
std::string path;
if(cmd_config)
path = args::get(cmd_config);
else
path = std::string(CONFIG_POI_CFG_PATH);

if(path.front() == '~')
path.replace(0, 1, QStandardPaths::writableLocation(QStandardPaths::HomeLocation).toStdString());

return path;
}();
spdlog::debug("Opening config file {}", config_path);
init_conf(config_path);

QVector<QPluginLoader *> plugins;
CommandHash_t pluginCommands;


Loading…
Cancel
Save