Cli#

Examples/Cli#
#include <SevenBit/DI.hpp>
#include <exception>
#include <iostream>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <vector>

using namespace sb::di;

struct IOptionHandler
{
    virtual std::string getName() = 0;

    virtual void handle(const std::string &value) = 0;

    virtual ~IOptionHandler() = default;
};

struct HelpOption final : public IOptionHandler
{
    std::string getName() { return "help"; }

    void handle(const std::string &value) { std::cout << "help commands ..." << std::endl; }
};

struct InfoOption final : public IOptionHandler
{
    std::string getName() { return "info"; }

    void handle(const std::string &value) { std::cout << "some info ..." << std::endl; }
};

struct NumberOption final : public IOptionHandler
{
    std::string getName() { return "number"; }

    void handle(const std::string &value) { std::cout << "number is: " << std::stoi(value) << std::endl; }
};

class CliHandler
{
    std::unordered_map<std::string, IOptionHandler *> _options;

  public:
    CliHandler(std::vector<IOptionHandler *> optionHandlers)
    {
        for (auto handler : optionHandlers)
        {
            _options[handler->getName()] = handler;
        }
    }

    void handle(int argc, char *argv[])
    {
        for (int i = 1; i < argc; ++i)
        {
            auto [name, value] = parseOption(argv[i]);
            if (auto it = _options.find(name); it != _options.end())
            {
                auto handler = it->second;
                handler->handle(value);
            }
        }
    }

  private:
    std::pair<std::string, std::string> parseOption(std::string option)
    {
        int idx = eatDashes(option);
        return {getName(idx, option), getValue(idx, option)};
    }

    int eatDashes(const std::string &option)
    {
        if (option.size() > 1 && option[0] == '-' && option[1] == '-')
        {
            return 2;
        }
        throw std::runtime_error("wrong option scheme use -- before each options");
    }

    std::string getName(int &index, const std::string &option)
    {
        int begin = index;
        while (index < option.size() && option[index] != '=')
        {
            index++;
        }
        auto name = option.substr(begin, index - begin);
        if (name.empty())
        {
            throw std::runtime_error("option cannot be empty");
        }
        return name;
    }

    std::string getValue(int &index, const std::string &option)
    {
        if (index < option.size() && option[index] == '=')
        {
            index++;
        }
        return option.substr(index);
    }
};

class Application
{
    CliHandler &_cliHandler;

  public:
    Application(CliHandler *cliHandler) : _cliHandler(*cliHandler) {}

    int run(int argc, char *argv[])
    {
        try
        {
            _cliHandler.handle(argc, argv);
        }
        catch (std::exception &e)
        {
            std::cout << e.what();
        }
        return 0;
    }
};
int main(int argc, char *argv[])
{
    IServiceProvider::Ptr provider = ServiceCollection{}
                                         .addSingleton<IOptionHandler, HelpOption>()
                                         .addSingleton<IOptionHandler, InfoOption>()
                                         .addSingleton<IOptionHandler, NumberOption>()
                                         .addSingleton<CliHandler>()
                                         .addScoped<Application>()
                                         .buildServiceProvider();

    Application &app = provider->getService<Application>();
    return app.run(argc, argv);
}