UsenetSearch/src/Application.cpp

316 lines
9.6 KiB
C++

/*
Copyright© 2021 John Sennesael
UsenetSearch is Free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
UsenetSearch is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with UsenetSearch. If not, see <https://www.gnu.org/licenses/>.
*/
#include <filesystem>
#include <iostream>
#include <string>
#include <vector>
#include "usenetsearch/StringUtils.h"
#include "usenetsearch/Application.h"
namespace usenetsearch {
void Application::AddBooleanOption(
char option,
const std::string& help,
std::function<void(bool)> onParse,
bool defaultValue)
{
auto val = std::make_shared<CommandLineOptionValue<bool>>();
val->type = CommandLineOptionType::Boolean;
val->option = option;
val->helpText = help;
val->value = defaultValue;
val->onParse = onParse;
m_commandLineArguments.emplace_back(std::move(val));
}
void Application::AddIntegerOption(
char option,
const std::string& help,
std::function<void(int)> onParse,
int defaultValue)
{
auto val = std::make_shared<CommandLineOptionValue<int>>();
val->type = CommandLineOptionType::Integer;
val->option = option;
val->helpText = help;
val->value = defaultValue;
val->onParse = onParse;
m_commandLineArguments.emplace_back(std::move(val));
}
void Application::AddFileOption(
char option,
const std::string& help,
std::function<void(std::filesystem::path)> onParse,
std::filesystem::path defaultValue)
{
auto val = std::make_shared<CommandLineOptionValue<
std::filesystem::path>>();
val->type = CommandLineOptionType::Path;
val->option = option;
val->helpText = help;
val->value = defaultValue;
val->onParse = onParse;
m_commandLineArguments.emplace_back(std::move(val));
}
void Application::AddStringOption(
char option,
const std::string& help,
std::function<void(std::string)> onParse,
std::string defaultValue)
{
auto val = std::make_shared<CommandLineOptionValue<
std::string>>();
val->type = CommandLineOptionType::String;
val->option = option;
val->helpText = help;
val->value = defaultValue;
val->onParse = onParse;
m_commandLineArguments.emplace_back(std::move(val));
}
void Application::Usage(const std::string& programName)
{
std::cout << "UsenetSearch - usenet search indexer" << std::endl;
std::cout << "Copyright© 2021 John Sennesael" << std::endl << std::endl;
std::cout << "Usage:" << std::endl << std::endl;
std::cout << programName;
std::cout << "\t";
// List default options
std::cout << "[-c <config filename>] ";
std::cout << "[-h] ";
// List custom options
for (const auto& optionValue: m_commandLineArguments)
{
switch (optionValue->type)
{
case CommandLineOptionType::Boolean:
std::cout << "[-" << optionValue->option << "] ";
break;
case CommandLineOptionType::Integer:
std::cout << "[-" << optionValue->option << " <number>] ";
break;
case CommandLineOptionType::Path:
std::cout << "[-" << optionValue->option << " <path>] ";
break;
case CommandLineOptionType::String:
std::cout << "[-" << optionValue->option << " <value>] ";
break;
}
}
std::cout << std::endl << std::endl;
// List standard detailed help
std::cout << "-c <file>\tSets configuration file to use" << std::endl;
std::cout << "-h\t\tShow help (this text)." << std::endl;
// List custom options detailed help
for (const auto& optionValue: m_commandLineArguments)
{
switch (optionValue->type)
{
case CommandLineOptionType::Boolean:
std::cout << "-" << optionValue->option << "\t"
<< optionValue->helpText << std::endl;
break;
case CommandLineOptionType::Integer:
std::cout << "-" << optionValue->option << " <number>\t"
<< optionValue->helpText << std::endl;
break;
case CommandLineOptionType::Path:
std::cout << "-" << optionValue->option << " <path>\t"
<< optionValue->helpText << std::endl;
break;
case CommandLineOptionType::String:
std::cout << "-" << optionValue->option << " <value>\t"
<< optionValue->helpText << std::endl;
break;
}
}
std::cout << std::endl;
}
Application::Application() : m_db(*this), m_filter(m_config)
{
std::cout.setf(std::ios::unitbuf);
}
bool Application::CanRun() const
{
return m_canRun;
}
Configuration& Application::GetConfig()
{
return m_config;
}
Database& Application::GetDb()
{
return m_db;
}
void Application::ExecuteCustomOption(
std::shared_ptr<CommandLineOption>& opt,
const std::string& value)
{
switch (opt->type)
{
case CommandLineOptionType::Boolean:
{
std::shared_ptr<CommandLineOptionValue<bool>> castedOption =
std::dynamic_pointer_cast<CommandLineOptionValue<bool>>(
opt
);
if (castedOption == nullptr)
{
throw std::runtime_error(
"Could not cast cli arg to the correct type."
);
}
castedOption->onParse(true);
}
break;
case CommandLineOptionType::Integer:
{
std::shared_ptr<CommandLineOptionValue<int>> castedOption =
std::dynamic_pointer_cast<CommandLineOptionValue<int>>(
opt
);
if (castedOption == nullptr)
{
throw std::runtime_error(
"Could not cast cli arg to the correct type."
);
}
castedOption->onParse(std::stoi(value));
}
break;
case CommandLineOptionType::String:
{
std::shared_ptr<CommandLineOptionValue<std::string>>
castedOption = std::dynamic_pointer_cast<
CommandLineOptionValue<std::string>>(
opt
);
if (castedOption == nullptr)
{
throw std::runtime_error(
"Could not cast cli arg to the correct type."
);
}
castedOption->onParse(value);
}
break;
case CommandLineOptionType::Path:
{
std::shared_ptr<CommandLineOptionValue<
std::filesystem::path>> castedOption =
std::dynamic_pointer_cast<
CommandLineOptionValue<
std::filesystem::path
>
>(opt);
if (castedOption == nullptr)
{
throw std::runtime_error(
"Could not cast cli arg to the correct type."
);
}
castedOption->onParse(value);
}
break;
}
}
Filter& Application::GetFilter()
{
return m_filter;
}
bool Application::Init(int argc, char* argv[])
{
ParseArgs(argc, argv);
if (!m_canRun) return false;
// Read config, setup db
m_config.Open(m_configFile);
m_db.MaxTreeDepth(m_config.MaxTreeDepth());
m_db.Open(m_config.DatabasePath());
m_filter.Init();
return true;
}
void Application::ParseArgs(int argc, char* argv[])
{
// Parse args.
for (int argn = 1; argn != argc; ++argn)
{
std::string curr_opt = argv[argn];
std::string next_opt = "";
if (argn+1 < argc) next_opt=argv[argn+1];
if (curr_opt == "-c")
{
if ((next_opt == "") or (StringStartsWith("-", next_opt)))
{
std::cerr << "Missing argument to -c option." << std::endl;
Usage(argv[0]);
return;
}
argn++;
m_configFile = argv[argn];
}
else if (curr_opt == "-h")
{
Usage(argv[0]);
return;
}
else
{
// Parse custom options.
bool parsed{false};
for (auto optionValue: m_commandLineArguments)
{
if ((std::string{"-"} + optionValue->option) == curr_opt)
{
if (optionValue->type == CommandLineOptionType::Boolean)
{
ExecuteCustomOption(optionValue);
}
else
{
argn++;
ExecuteCustomOption(optionValue, argv[argn]);
}
parsed = true;
}
}
if (!parsed)
{
std::cerr << "Invalid option: " << curr_opt << std::endl;
Usage(argv[0]);
return;
}
}
}
m_canRun = true;
}
} // namespace usenetsearch