• ArgParser.cpp
  • #include "../include/ArgParser.hpp"
    
    #include <algorithm>
    #include <cctype>
    #include <iostream>
    #include <string>
    
    using namespace ArgParser;
    
    #ifdef __linux__ 
    #include <fstream> 
    ParseArgument::ParseArgument(std::string const& arg_prec) : arg_prec(arg_prec) {
        AddArgument("h", "help", "Prints the help text.", [&]() {
            help();
        });
        std::ifstream cmdlineFile("/proc/self/cmdline");
        std::getline(cmdlineFile, program_name, '\0'); 
        for(std::string line; std::getline(cmdlineFile, line, '\0');) {
            arg_list.emplace_back(line);
        }
    }
    #elif _WIN32
    #include <windows.h>
    #include <shellapi.h>
    std::string wstring_to_string(const std::wstring& str) {
        return {str.begin(), str.end()};
    }
    
    ParseArgument::ParseArgument(std::string const& arg_prec) : arg_prec(arg_prec) {
        AddArgument("h", "help", "Prints the help text.", [&]() {
            help();
        });
    
        int argc = 0; 
        LPWSTR * argv = CommandLineToArgvW(GetCommandLineW(), &argc);
        program_name = wstring_to_string(std::wstring(argv[0]));
        
        for(int i = 1; i < argc; i++) {
            arg_list.emplace_back(wstring_to_string(std::wstring(argv[i]))); 
        }
    }
    #else 
        static_assert(false, "Unsupported OS!"); 
    #endif
    
    void ParseArgument::SetHelp(std::string const& help) {
        help_text.clear(); 
        help_text << help;
        default_help_text = false;
    }
    
    void ParseArgument::SetErrorText(ErrorType type, std::string const& error_message) {
        switch(type) {
            case ErrorType::MissingValue:
                Errors.missing_value = error_message; 
            break;
            case ErrorType::KeyExists:
                Errors.key_exists = error_message; 
            break;
        }
    }
    
    void ParseArgument::HandleArguments() {
        if(arg_list.empty()) {
            return; 
        }
        
        for(auto it = arg_list.begin() + 1; it != arg_list.end(); it++) {
            if(*it == arg_prec + "h" or *it == (arg_prec + arg_prec) + "help") {
                help(); 
            }
        }
    
        for(auto it = arg_list.begin(); it != arg_list.end(); it++) {
            for(auto &[key, handler] : user_defined_arg_list) {
                auto& [names, data] = key; 
    
                if(not names.does_match(*it, arg_prec)) {
                    continue; 
                }
    
                if(data.has_value) {
                    if(it == arg_list.end()) {        
                        std::cerr << Errors.missing_value << std::endl; 
                        exit(1);
                    }
    
                    data.value = *(++it);
                    auto type = handler->GetType(); 
    
                    if (type == "int") {
                        std::for_each(data.value.begin(), data.value.end(), [names](char ch) {
                            if (not std::isdigit(ch)) {
                                std::cerr << "For given argument \"" << names.get_matched() << "\" expected value type was integer." << std::endl;  
                                exit(1); 
                            }
                        }); 
                        static_cast<ParamHandler<int>*>(handler)->Invoke(std::stoi(data.value));
                        break;
                    }
    
                    if (type == "float") {
                        bool dot_found = false; 
                        std::for_each(data.value.begin(), data.value.end(), [&dot_found, names](char ch) {
                            if (std::isdigit(ch)) {
                                return; 
                            }
                            if (ch == '.') {
                                if (dot_found) {
                                    std::cerr << "For given argument \"" << names.get_matched() << "\" expected value type was float. But found multiple points." << std::endl;  
                                    exit(1); 
                                }
                                dot_found = true; 
                                return; 
                            }
                            std::cerr << "For given argument \"" << names.get_matched() << "\" expected value type was float." << std::endl;  
                            exit(1); 
                        });
                        static_cast<ParamHandler<float>*>(handler)->Invoke(std::stof(data.value));
                        break;
                    }
    
                    if (type == "bool") {
                        std::array<std::string, 6> valid_responses{"true", "false", "on", "off", "1", "0"};
                        bool valid = false;
                        for(auto valid_response : valid_responses) {
                            if (data.value == valid_response) {
                                valid = true;
                                if (valid_response == "true" or valid_response == "on" or valid_response == "1") {
                                    static_cast<ParamHandler<bool>*>(handler)->Invoke(true);
                                } else {
                                    static_cast<ParamHandler<bool>*>(handler)->Invoke(false);
                                }
                                break;
                            }
                        }
                        
                        if (not valid) {
                            std::cerr << "For given argument \"" << names.get_matched() << "\" expected value type was boolean (accepted: true/false, on/off, 1/0)" << std::endl;
                            exit(1);
                        }
    
                        break;
                    }
                    if (type == "string") {
                        static_cast<ParamHandler<std::string>*>(handler)->Invoke(data.value);
                        break;
                    }
                    
                }
                static_cast<NoParamHandler*>(handler)->Invoke();
            }
        }
    }
    
    std::pair<std::string, std::string> ParseArgument::split(std::string const& str, char delim) {
        auto pos = str.find(delim); 
        if(pos == std::string::npos) {
            throw MissingValue(Errors.missing_value); 
        }
        return {str.substr(0, pos), str.substr(pos + 1)}; 
    }
    
    
    std::pair<bool, bool> ParseArgument::IsKeyExists(std::pair<std::string, std::string> const& keys) {
        for(auto &[arg_keys, arg_handler] : user_defined_arg_list) {
            if(arg_keys.name._short == keys.first or arg_keys.name._long == keys.second) {
                return {arg_keys.name._short == keys.first, arg_keys.name._long == keys.second};
            }
        }
        return {false, false}; 
    }
    
    void ParseArgument::help() {
        std::cout << "Usage: " << program_name << " " << usage.str() << std::endl; 
        std::cout << "Options:" << std::endl; 
        for(std::string line; std::getline(help_text, line);) {
            std::cout << "  " << line << std::endl; 
        }
        exit(0);
    }
    
    void ParseArgument::prepare_help_text_from_long_key(std::string const& argument_short, std::string const& argument_long, bool has_value) {
        help_text << arg_prec << argument_short << (has_value ? " <value>" : "") << ", " << arg_prec << arg_prec << argument_long << (has_value ? " <value>" : "") << " : ";
    
        for(auto ch : argument_long) {
            if(ch == '-' or ch == '_') { help_text << " "; continue; }
            help_text << (char)std::toupper(ch);
        }
    
        help_text << "\n"; 
    }