2023-10-06 18:39:07 -03:00
|
|
|
#include "settings.h"
|
|
|
|
|
2023-10-07 19:36:20 -03:00
|
|
|
static const char* defaultConfig =
|
|
|
|
#include "generated/default.conf"
|
|
|
|
;
|
|
|
|
|
2023-10-09 18:47:00 -03:00
|
|
|
enum class Flag {
|
|
|
|
config,
|
|
|
|
help,
|
|
|
|
none
|
|
|
|
};
|
|
|
|
|
|
|
|
enum class Option {
|
|
|
|
level,
|
|
|
|
type,
|
|
|
|
source,
|
|
|
|
destination,
|
|
|
|
parallel,
|
|
|
|
_optionsSize
|
|
|
|
};
|
|
|
|
|
|
|
|
using Literals = std::array<std::string_view, 2>;
|
|
|
|
constexpr std::array<Literals, static_cast<int>(Flag::none)> flags({{
|
|
|
|
{"-c", "--config"},
|
|
|
|
{"-h", "--help"}
|
|
|
|
}});
|
|
|
|
|
2023-10-06 18:39:07 -03:00
|
|
|
constexpr std::array<std::string_view, Settings::_actionsSize> actions({
|
|
|
|
"convert",
|
|
|
|
"help",
|
2023-10-07 19:36:20 -03:00
|
|
|
"config"
|
|
|
|
});
|
|
|
|
|
2023-10-09 18:47:00 -03:00
|
|
|
constexpr std::array<std::string_view, static_cast<int>(Option::_optionsSize)> options({
|
2023-10-07 19:36:20 -03:00
|
|
|
"level",
|
2023-10-09 18:47:00 -03:00
|
|
|
"type",
|
|
|
|
"source",
|
|
|
|
"destination",
|
|
|
|
"parallel"
|
2023-10-06 18:39:07 -03:00
|
|
|
});
|
|
|
|
|
2023-10-07 19:36:20 -03:00
|
|
|
constexpr std::array<std::string_view, Settings::_typesSize> types({
|
|
|
|
"mp3"
|
|
|
|
});
|
|
|
|
|
|
|
|
bool is_space(char ch){
|
|
|
|
return std::isspace(static_cast<unsigned char>(ch));
|
|
|
|
}
|
|
|
|
|
2023-10-09 18:47:00 -03:00
|
|
|
Flag getFlag(const std::string_view arg) {
|
2023-10-10 13:24:46 -03:00
|
|
|
for (unsigned char i = 0; i < flags.size(); ++i) {
|
2023-10-09 18:47:00 -03:00
|
|
|
const Literals& lit = flags[i];
|
|
|
|
unsigned char dist = std::distance(lit.begin(), std::find(lit.begin(), lit.end(), arg));
|
|
|
|
if (dist < lit.size())
|
|
|
|
return static_cast<Flag>(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Flag::none;
|
|
|
|
}
|
|
|
|
|
|
|
|
Option stringToOption(const std::string& source) {
|
|
|
|
unsigned char dist = std::distance(options.begin(), std::find(options.begin(), options.end(), source));
|
|
|
|
if (dist < static_cast<unsigned char>(Option::_optionsSize))
|
|
|
|
return static_cast<Option>(dist);
|
|
|
|
|
|
|
|
return Option::_optionsSize;
|
|
|
|
}
|
|
|
|
|
2023-10-06 18:39:07 -03:00
|
|
|
Settings::Settings(int argc, char ** argv):
|
|
|
|
arguments(),
|
|
|
|
action(std::nullopt),
|
2023-10-07 19:36:20 -03:00
|
|
|
outputType(std::nullopt),
|
2023-10-06 18:39:07 -03:00
|
|
|
input(std::nullopt),
|
|
|
|
output(std::nullopt),
|
|
|
|
logLevel(std::nullopt)
|
|
|
|
{
|
|
|
|
for (int i = 1; i < argc; ++i)
|
|
|
|
arguments.push_back(argv[i]);
|
|
|
|
|
|
|
|
parseArguments();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Settings::parseArguments() {
|
2023-10-09 18:47:00 -03:00
|
|
|
Flag flag = Flag::none;
|
2023-10-10 13:24:46 -03:00
|
|
|
for (unsigned int i = 0; i < arguments.size(); ++i) {
|
2023-10-06 18:39:07 -03:00
|
|
|
const std::string_view& arg = arguments[i];
|
|
|
|
if (i == 0) {
|
2023-10-09 18:47:00 -03:00
|
|
|
Action act = stringToAction(arg);
|
2023-10-07 19:36:20 -03:00
|
|
|
if (act < _actionsSize) {
|
|
|
|
action = act;
|
2023-10-06 18:39:07 -03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-09 18:47:00 -03:00
|
|
|
switch (flag) {
|
|
|
|
case Flag::config:
|
|
|
|
configPath = arg;
|
|
|
|
flag = Flag::none;
|
|
|
|
continue;
|
|
|
|
case Flag::none:
|
|
|
|
flag = getFlag(arg);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (flag) {
|
|
|
|
case Flag::none:
|
|
|
|
break;
|
|
|
|
case Flag::help:
|
|
|
|
action = help;
|
|
|
|
flag = Flag::none;
|
|
|
|
continue;
|
|
|
|
default:
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2023-10-06 18:39:07 -03:00
|
|
|
if (getAction() == convert) {
|
|
|
|
if (!input.has_value()) {
|
|
|
|
input = arg;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!output.has_value()) {
|
2023-10-08 20:29:40 -03:00
|
|
|
output = arg;
|
2023-10-06 18:39:07 -03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Settings::Action Settings::getAction() const {
|
|
|
|
if (action.has_value())
|
|
|
|
return action.value();
|
|
|
|
else
|
|
|
|
return convert;
|
|
|
|
}
|
|
|
|
|
2023-10-07 19:36:20 -03:00
|
|
|
Settings::Type Settings::getType() const {
|
|
|
|
if (outputType.has_value())
|
|
|
|
return outputType.value();
|
|
|
|
else
|
|
|
|
return mp3;
|
|
|
|
}
|
|
|
|
|
2023-10-06 18:39:07 -03:00
|
|
|
std::string Settings::getInput() const {
|
|
|
|
if (input.has_value())
|
2023-10-09 18:47:00 -03:00
|
|
|
return resolvePath(input.value());
|
2023-10-06 18:39:07 -03:00
|
|
|
else
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string Settings::getOutput() const {
|
|
|
|
if (output.has_value())
|
2023-10-09 18:47:00 -03:00
|
|
|
return resolvePath(output.value());
|
2023-10-06 18:39:07 -03:00
|
|
|
else
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
2023-10-07 19:36:20 -03:00
|
|
|
std::string Settings::getConfigPath() const {
|
|
|
|
if (configPath.has_value())
|
2023-10-09 18:47:00 -03:00
|
|
|
return resolvePath(configPath.value());
|
2023-10-07 19:36:20 -03:00
|
|
|
else
|
2023-10-09 18:47:00 -03:00
|
|
|
return resolvePath("~/.config/mlc.conf");
|
2023-10-07 19:36:20 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Settings::isConfigDefault() const {
|
|
|
|
return !configPath.has_value();
|
|
|
|
}
|
|
|
|
|
2023-10-08 20:29:40 -03:00
|
|
|
Logger::Severity Settings::getLogLevel() const {
|
2023-10-06 18:39:07 -03:00
|
|
|
if (logLevel.has_value())
|
|
|
|
return logLevel.value();
|
|
|
|
else
|
2023-10-08 20:29:40 -03:00
|
|
|
return Logger::Severity::info;
|
2023-10-06 18:39:07 -03:00
|
|
|
}
|
|
|
|
|
2023-10-09 18:47:00 -03:00
|
|
|
unsigned int Settings::getThreads() const {
|
|
|
|
if (threads.has_value())
|
|
|
|
return threads.value();
|
|
|
|
else
|
|
|
|
return 0;
|
2023-10-06 18:39:07 -03:00
|
|
|
}
|
2023-10-07 19:36:20 -03:00
|
|
|
|
|
|
|
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);
|
2023-10-09 18:47:00 -03:00
|
|
|
if (option == Option::_optionsSize)
|
2023-10-07 19:36:20 -03:00
|
|
|
return;
|
|
|
|
|
|
|
|
switch (option) {
|
2023-10-09 18:47:00 -03:00
|
|
|
case Option::level: {
|
2023-10-07 19:36:20 -03:00
|
|
|
std::string lv;
|
2023-10-09 18:47:00 -03:00
|
|
|
if (!logLevel.has_value() && stream >> lv) {
|
2023-10-08 20:29:40 -03:00
|
|
|
Logger::Severity level = Logger::stringToSeverity(lv);
|
|
|
|
if (level < Logger::Severity::_sevetirySize)
|
2023-10-07 19:36:20 -03:00
|
|
|
logLevel = level;
|
|
|
|
}
|
|
|
|
} break;
|
2023-10-09 18:47:00 -03:00
|
|
|
case Option::type: {
|
2023-10-07 19:36:20 -03:00
|
|
|
std::string lv;
|
2023-10-09 18:47:00 -03:00
|
|
|
if (!outputType.has_value() && stream >> lv) {
|
2023-10-07 19:36:20 -03:00
|
|
|
Type type = stringToType(lv);
|
|
|
|
if (type < _typesSize)
|
|
|
|
outputType = type;
|
|
|
|
}
|
|
|
|
} break;
|
2023-10-09 18:47:00 -03:00
|
|
|
case Option::source: {
|
|
|
|
std::string path;
|
|
|
|
if (stream >> path) {
|
|
|
|
if (!input.has_value()) {
|
|
|
|
input = path;
|
|
|
|
} else if (!output.has_value()) {
|
|
|
|
output = input;
|
|
|
|
input = path;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case Option::destination: {
|
|
|
|
std::string path;
|
|
|
|
if (!output.has_value() && stream >> path)
|
|
|
|
output = path;
|
|
|
|
} break;
|
|
|
|
case Option::parallel: {
|
|
|
|
unsigned int count;
|
|
|
|
if (!threads.has_value() && stream >> count)
|
|
|
|
threads = count;
|
|
|
|
} break;
|
2023-10-07 19:36:20 -03:00
|
|
|
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<Action>(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<Action>(dist);
|
|
|
|
|
|
|
|
return _actionsSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
Settings::Type Settings::stringToType(const std::string& source) {
|
|
|
|
unsigned char dist = std::distance(types.begin(), std::find(types.begin(), types.end(), source));
|
|
|
|
if (dist < _typesSize)
|
|
|
|
return static_cast<Type>(dist);
|
|
|
|
|
|
|
|
return _typesSize;
|
|
|
|
}
|
2023-10-09 18:47:00 -03:00
|
|
|
|
|
|
|
std::string Settings::resolvePath(const std::string& line) {
|
|
|
|
if (line.size() > 0 && line[0] == '~')
|
|
|
|
return getenv("HOME") + line.substr(1);
|
|
|
|
else
|
|
|
|
return line;
|
|
|
|
}
|