From 6c7356598aefb8c2fb9d62c3f5e0a505e16acf13 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 7 Oct 2023 19:36:20 -0300 Subject: [PATCH] some more thoughts about config --- src/CMakeLists.txt | 10 ++++ src/collection.cpp | 9 ++- src/default.conf | 23 +++++++ src/loggable.cpp | 18 ++++++ src/loggable.h | 8 ++- src/main.cpp | 27 ++++++--- src/settings.cpp | 143 ++++++++++++++++++++++++++++++++++++++++++-- src/settings.h | 34 ++++++++++- src/taskmanager.cpp | 27 +++++++-- src/taskmanager.h | 16 +++-- 10 files changed, 284 insertions(+), 31 deletions(-) create mode 100644 src/default.conf diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index baca389..b526755 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,3 +1,10 @@ +function(make_includable input_file output_file) + file(READ ${input_file} content) + set(delim "for_c++_include") + set(content "R\"${delim}(\n${content})${delim}\"") + file(WRITE ${output_file} "${content}") +endfunction(make_includable) + set(SOURCES main.cpp help.cpp @@ -20,3 +27,6 @@ set(HEADERS ) target_sources(${PROJECT_NAME} PRIVATE ${SOURCES}) + +make_includable(default.conf ${CMAKE_BINARY_DIR}/generated/default.conf) +target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_BINARY_DIR}) diff --git a/src/collection.cpp b/src/collection.cpp index 39bee9b..c71b257 100644 --- a/src/collection.cpp +++ b/src/collection.cpp @@ -71,13 +71,12 @@ void Collection::convert(const std::string& outPath) { switch (entry.status().type()) { case fs::file_type::regular: { fs::path sourcePath = entry.path(); - fs::path dstPath = out / sourcePath.filename(); - if (isMusic(sourcePath)) { - dstPath.replace_extension(".mp3"); + fs::path dstPath = out / sourcePath.stem(); + if (isMusic(sourcePath)) taskManager->queueJob(sourcePath, dstPath); - } else { + else fs::copy_file(sourcePath, dstPath, fs::copy_options::overwrite_existing); - } + //std::cout << sourcePath << " => " << dstPath << std::endl; } break; case fs::file_type::directory: { diff --git a/src/default.conf b/src/default.conf new file mode 100644 index 0000000..8f23d3c --- /dev/null +++ b/src/default.conf @@ -0,0 +1,23 @@ +# This is a default autogenerated MLC config +# The syntax goes like this: +# key value +# +# Use # sign for comments +# +# All printed comented out values are default values +# +# You should have one of this files as ~/.config/mlc.conf, +# if you have started mlc at least once. +# This is going to be used every time you launch mlc, +# so, edit it if you wish to change default behaviour. +# +# Alternatively, you can launch `mlc -c customConfig.conf` +# if you wish to override ~/.config/mlc.conf file + +# Log level, regulates minimal message severity to be printed +# Allowed values are: debug, info, minor, major, warning, error, fatal +#level info + +# Output type +# Allowed values are: mp3 (more comming soon) +#type mp3 diff --git a/src/loggable.cpp b/src/loggable.cpp index da551c9..c0bd2e0 100644 --- a/src/loggable.cpp +++ b/src/loggable.cpp @@ -1,5 +1,15 @@ #include "loggable.h" +constexpr std::array levels({ + "debug", + "info", + "minor", + "major", + "warning", + "error", + "fatal" +}); + Loggable::Loggable(Loggable::Severity severity): currentSeverity(severity), history() @@ -16,3 +26,11 @@ void Loggable::log(Loggable::Severity severity, const std::string& comment) cons std::list> Loggable::getHistory() const { return history; } + +Loggable::Severity Loggable::stringToSeverity(const std::string& line) { + unsigned char dist = std::distance(levels.begin(), std::find(levels.begin(), levels.end(), line)); + if (dist < _sevetirySize) + return static_cast(dist); + + return _sevetirySize; +} diff --git a/src/loggable.h b/src/loggable.h index 9b7ba78..999defe 100644 --- a/src/loggable.h +++ b/src/loggable.h @@ -2,6 +2,9 @@ #include #include +#include +#include +#include class Loggable { public: @@ -12,7 +15,8 @@ public: major, warning, error, - fatal + fatal, + _sevetirySize }; typedef std::pair Message; @@ -22,6 +26,8 @@ public: void log (Severity severity, const std::string& comment) const; std::list getHistory() const; + static Severity stringToSeverity(const std::string& line); + private: const Severity currentSeverity; mutable std::list history; diff --git a/src/main.cpp b/src/main.cpp index 6b2d6de..35df94e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,8 @@ #include +#include #include #include +#include #include #include "FLAC/stream_decoder.h" @@ -12,14 +14,14 @@ #include "settings.h" int main(int argc, char **argv) { - Settings settings(argc, argv); + std::shared_ptr settings = std::make_shared(argc, argv); - switch (settings.getAction()) { + switch (settings->getAction()) { case Settings::help: printHelp(); return 0; - case Settings::printConfig: - //printHelp(); + case Settings::config: + std::cout << settings->defaultConfig() << std::endl; return 0; case Settings::convert: std::cout << "Converting..." << std::endl; @@ -29,12 +31,23 @@ int main(int argc, char **argv) { return -1; } - TaskManager taskManager; + if (!settings->readConfigFile() && settings->isConfigDefault()) { + std::string defaultConfigPath = settings->getConfigPath(); + std::ofstream file(defaultConfigPath, std::ios::out | std::ios::trunc); + if (file.is_open()) { + std::cout << "Writing default config to " << defaultConfigPath << std::endl; + file << settings->defaultConfig(); + } else { + std::cout << "Couldn't open " << defaultConfigPath << " to write default config" << std::endl; + } + } + + TaskManager taskManager(settings); taskManager.start(); std::chrono::time_point start = std::chrono::system_clock::now(); - Collection collection(settings.getInput(), &taskManager); - collection.convert(settings.getOutput()); + Collection collection(settings->getInput(), &taskManager); + collection.convert(settings->getOutput()); taskManager.printProgress(); taskManager.wait(); diff --git a/src/settings.cpp b/src/settings.cpp index a524081..d519f37 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1,14 +1,32 @@ #include "settings.h" +static const char* defaultConfig = +#include "generated/default.conf" +; + constexpr std::array actions({ "convert", "help", - "printConfig" + "config" }); +constexpr std::array options({ + "level", + "type" +}); + +constexpr std::array types({ + "mp3" +}); + +bool is_space(char ch){ + return std::isspace(static_cast(ch)); +} + Settings::Settings(int argc, char ** argv): arguments(), action(std::nullopt), + outputType(std::nullopt), input(std::nullopt), output(std::nullopt), logLevel(std::nullopt) @@ -23,10 +41,9 @@ void Settings::parseArguments() { for (int i = 0; i < arguments.size(); ++i) { const std::string_view& arg = arguments[i]; if (i == 0) { - std::string_view act = stripFlags(arg); - int dist = std::distance(actions.begin(), std::find(actions.begin(), actions.end(), act)); - if (dist < _actionsSize) { - action = static_cast(dist); + Action act = stringToAction(stripFlags(arg)); + if (act < _actionsSize) { + action = act; continue; } } @@ -52,6 +69,13 @@ Settings::Action Settings::getAction() const { return convert; } +Settings::Type Settings::getType() const { + if (outputType.has_value()) + return outputType.value(); + else + return mp3; +} + std::string Settings::getInput() const { if (input.has_value()) return input.value(); @@ -66,6 +90,17 @@ std::string Settings::getOutput() const { return ""; } +std::string Settings::getConfigPath() const { + if (configPath.has_value()) + return configPath.value(); + else + return std::string(getenv("HOME")) + "/.config/mlc.conf"; +} + +bool Settings::isConfigDefault() const { + return !configPath.has_value(); +} + Loggable::Severity Settings::getLogLevel() const { if (logLevel.has_value()) return logLevel.value(); @@ -82,3 +117,101 @@ std::string_view Settings::stripFlags(const std::string_view& option) { } return option; } + +void Settings::strip(std::string& line) { + line.erase(line.begin(), std::find_if(line.begin(), line.end(), std::not_fn(is_space))); + line.erase(std::find_if(line.rbegin(), line.rend(), std::not_fn(is_space)).base(), line.end()); +} + +void Settings::stripComment(std::string& line) { + std::string::size_type index = line.find('#'); + if (index != std::string::npos) + line.erase(index); + + strip(line); +} + +std::string Settings::defaultConfig() const { + return ::defaultConfig + 1; +} + +bool Settings::readConfigFile() { + std::ifstream file; + file.open(getConfigPath(), std::ios::in); + if (file.is_open()){ + std::string tp; + while(getline(file, tp)) { + stripComment(tp); + if (!tp.empty()) + readConfigLine(tp); + } + file.close(); + return true; + } + + return false; +} + +void Settings::readConfigLine(const std::string& line) { + std::istringstream stream(line); + std::string key; + if (!(stream >> key)) + return; + + Option option = stringToOption(key); + if (option == _optionsSize) + return; + + switch (option) { + case level: { + std::string lv; + if (stream >> lv) { + Loggable::Severity level = Loggable::stringToSeverity(lv); + if (level < Loggable::_sevetirySize) + logLevel = level; + } + } break; + case type: { + std::string lv; + if (stream >> lv) { + Type type = stringToType(lv); + if (type < _typesSize) + outputType = type; + } + } break; + default: + break; + } +} + +Settings::Action Settings::stringToAction(const std::string& source) { + unsigned char dist = std::distance(actions.begin(), std::find(actions.begin(), actions.end(), source)); + if (dist < _actionsSize) + return static_cast(dist); + + return _actionsSize; +} + +Settings::Action Settings::stringToAction(const std::string_view& source) { + unsigned char dist = std::distance(actions.begin(), std::find(actions.begin(), actions.end(), source)); + if (dist < _actionsSize) + return static_cast(dist); + + return _actionsSize; +} + +Settings::Option Settings::stringToOption(const std::string& source) { + unsigned char dist = std::distance(options.begin(), std::find(options.begin(), options.end(), source)); + if (dist < _optionsSize) + return static_cast