diff --git a/Hazelnupp.vpp b/Hazelnupp.vpp index 1be4fc1..772a7f4 100644 Binary files a/Hazelnupp.vpp and b/Hazelnupp.vpp differ diff --git a/Hazelnupp/DataType.h b/Hazelnupp/DataType.h index b045ee4..2ed4970 100644 --- a/Hazelnupp/DataType.h +++ b/Hazelnupp/DataType.h @@ -1,4 +1,5 @@ #pragma once +#include namespace Hazelnp { @@ -12,4 +13,27 @@ namespace Hazelnp STRING, LIST }; + + static inline std::string DataTypeToString(DATA_TYPE type) + { + switch (type) + { + case DATA_TYPE::VOID: + return "VOID"; + + case DATA_TYPE::INT: + return "INT"; + + case DATA_TYPE::FLOAT: + return "FLOAT"; + + case DATA_TYPE::STRING: + return "STRING"; + + case DATA_TYPE::LIST: + return "LIST"; + } + + return ""; + } } diff --git a/Hazelnupp/Hazelnupp.cpp b/Hazelnupp/Hazelnupp.cpp index 195555a..1e09648 100644 --- a/Hazelnupp/Hazelnupp.cpp +++ b/Hazelnupp/Hazelnupp.cpp @@ -60,12 +60,15 @@ void Hazelnupp::Parse(const int argc, const char* const* argv) // Apply constraints such as default values, and required parameters. // Types have already been enforced. - ApplyConstraints(); + // Dont apply constraints when we are just printind the param docs + if ((!catchHelp) || (!HasParam("--help"))) + ApplyConstraints(); } catch (const HazelnuppConstraintTypeMissmatch& hctm) { if (crashOnFail) { + std::cout << GenerateDocumentation() << std::endl; std::cerr << "Fatal error: Command-line parameter value-type mismatch at \"" << hctm.What() << "\"!"; quick_exit(-1009); } @@ -76,6 +79,7 @@ void Hazelnupp::Parse(const int argc, const char* const* argv) { if (crashOnFail) { + std::cout << GenerateDocumentation() << std::endl; std::cerr << "Fatal error: Missing required command-line parameter \"" << hctm.What() << "\"!"; quick_exit(-1010); } @@ -83,6 +87,13 @@ void Hazelnupp::Parse(const int argc, const char* const* argv) throw hctm; // yeet } + // Catch --help parameter + if ((catchHelp) && (HasParam("--help"))) + { + std::cout << GenerateDocumentation() << std::endl; + quick_exit(0); + } + return; } @@ -281,6 +292,162 @@ bool Hazelnupp::GetCrashOnFail() const return crashOnFail; } +void Hazelnupp::SetCatchHelp(bool catchHelp) +{ + this->catchHelp = catchHelp; + return; +} + +bool Hazelnupp::GetCatchHelp() const +{ + return catchHelp; +} + +void Hazelnupp::SetBriefDescription(const std::string& description) +{ + briefDescription = description; + return; +} + +const std::string& Hazelnupp::GetBriefDescription() +{ + return briefDescription; +} + +void Hazelnp::Hazelnupp::RegisterDescription(const std::string& parameter, const std::string& description) +{ + parameterDescriptions[parameter] = description; + return; +} + +const std::string Hazelnp::Hazelnupp::GetDescription(const std::string& parameter) const +{ + // Do we already have a description for this parameter? + const auto par = parameterDescriptions.find(parameter); + if (par == parameterDescriptions.end()) + // No? Then return "" + return ""; + + // We do? Then return it + return par->second; +} + +void Hazelnp::Hazelnupp::ClearDescription(const std::string& parameter) +{ + // This will just do nothing if the entry does not exist + parameterDescriptions.erase(parameter); + return; +} + +std::string Hazelnupp::GenerateDocumentation() const +{ + std::stringstream ss; + + // Add brief, if available + if (briefDescription.length() > 0) + ss << briefDescription << std::endl; + + // Collect parameter information + struct ParamDocEntry + { + std::string abbreviation; + std::string description; + std::string type; + bool required = false; + bool typeIsForced = false; + std::string defaultVal; + }; + std::unordered_map paramInfos; + + // Collect descriptions + for (const auto& it : parameterDescriptions) + { + // Do we already have that param in the paramInfo set? + if (paramInfos.find(it.first) == paramInfos.end()) + // No? Create it. + paramInfos[it.first] = ParamDocEntry(); + + paramInfos[it.first].description = it.second; + } + + // Collect abbreviations + // first value is abbreviation, second is long form + for (const auto& it : abbreviations) + { + // Do we already have that param in the paramInfo set? + if (paramInfos.find(it.second) == paramInfos.end()) + // No? Create it. + paramInfos[it.second] = ParamDocEntry(); + + paramInfos[it.second].abbreviation = it.first; + } + + // Collect constraints + for (const auto& it : constraints) + { + // Do we already have that param in the paramInfo set? + if (paramInfos.find(it.first) == paramInfos.end()) + // No? Create it. + paramInfos[it.first] = ParamDocEntry(); + + ParamDocEntry& cached = paramInfos[it.first]; + cached.required = it.second.required; + cached.typeIsForced = it.second.constrainType; + cached.type = DataTypeToString(it.second.wantedType); + + std::stringstream defaultValueSs; + for (const std::string& s : it.second.defaultValue) + { + defaultValueSs << '\'' << s << '\''; + + // Add a space if we are not at the last entry + if ((void*)&s != (void*)&it.second.defaultValue.back()) + defaultValueSs << " "; + } + cached.defaultVal = defaultValueSs.str(); + } + + // Now generate the documentatino body + if (paramInfos.size() > 0) + { + ss << std::endl + << "==== AVAILABLE PARAMETERS ====" + << std::endl << std::endl; + + for (const auto& it : paramInfos) + { + const ParamDocEntry& pde = it.second; + + // Put name + ss << it.first << " "; + + // Put abbreviation + if (pde.abbreviation.length() > 0) + ss << pde.abbreviation << " "; + + // Put type + if (pde.typeIsForced) + ss << pde.type << " "; + + // Put default value + if (pde.defaultVal.length() > 0) + ss << "default=[" << pde.defaultVal << "] "; + + // Put required tag, but only if no default value + if ((pde.required) && (pde.defaultVal.length() == 0)) + ss << "[[REQUIRED]] "; + + // Put brief description + if (pde.description.length() > 0) + ss << pde.description; + + ss << std::endl << std::endl; + } + } + + return ss.str(); +} + void Hazelnupp::ApplyConstraints() { // Enforce required parameters / default values diff --git a/Hazelnupp/Hazelnupp.h b/Hazelnupp/Hazelnupp.h index 47f6f84..96e2dc6 100644 --- a/Hazelnupp/Hazelnupp.h +++ b/Hazelnupp/Hazelnupp.h @@ -54,6 +54,32 @@ namespace Hazelnp //! Gets whether the application crashes on an exception whilst parsing, and prints to stderr. bool GetCrashOnFail() const; + //! Sets whether the Hazelnupp should automatically catch the --help parameter, print the parameter documentation to stdout, and exit or not. + void SetCatchHelp(bool catchHelp); + + //! Retruns whether the Hazelnupp should automatically catch the --help parameter, print the parameter documentation to stdout, and exit or not. + bool GetCatchHelp() const; + + //! Sets a brief description of the application to be automatically added to the documentation. + void SetBriefDescription(const std::string& description); + + //! Returns the brief description of the application to be automatically added to the documentation. + const std::string& GetBriefDescription(); + + //! Willl register a short description for a parameter. + //! Will overwrite existing descriptions for that parameter. + void RegisterDescription(const std::string& parameter, const std::string& description); + + //! Will return a short description for a parameter, if it exists. + //! Empty string if it does not exist. + const std::string GetDescription(const std::string& parameter) const; + + //! Will delete the description of a parameter if it exists. + void ClearDescription(const std::string& parameter); + + //! Will generate a text-based documentation suited to show the user, for example on --help. + std::string GenerateDocumentation() const; + private: //! Will translate the c-like args to an std::vector void PopulateRawArgs(const int argc, const char* const* argv); @@ -76,14 +102,24 @@ namespace Hazelnp std::string executableName; //! The path of the executable. Always argv[0] std::unordered_map parameters; - // These are abbreviations. Like, -f for --force. + //! These are abbreviations. Like, -f for --force. std::unordered_map abbreviations; - // Parameter constraints, mapped to keys + //! Parameter constraints, mapped to keys std::unordered_map constraints; + //! Raw argv std::vector rawArgs; + //! Short descriptions for parameters + std::unordered_map parameterDescriptions; + + //! A brief description of the application to be added to the generated documentation. Optional. + std::string briefDescription; + + //! If set to true, Hazelnupp will automatically catch the --help parameter, print the parameter documentation to stdout and exit. + bool catchHelp = true; + //! If set to true, Hazelnupp will crash the application with output to stderr when an exception is thrown whilst parsing. bool crashOnFail = true; }; diff --git a/Hazelnupp/Hazelnupp.vcxproj b/Hazelnupp/Hazelnupp.vcxproj index 2cb5ad0..88ae8dc 100644 --- a/Hazelnupp/Hazelnupp.vcxproj +++ b/Hazelnupp/Hazelnupp.vcxproj @@ -27,26 +27,26 @@ - StaticLibrary + Application true v142 Unicode - StaticLibrary + Application false v142 true Unicode - StaticLibrary + Application true v142 Unicode - StaticLibrary + Application false v142 true