1 // Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner 2 // under NSF AWARD 1414736 and by the respective contributors. 3 // All rights reserved. 4 // 5 // SPDX-License-Identifier: BSD-3-Clause 6 7 #pragma once 8 9 // [CLI11:public_includes:set] 10 #include <algorithm> 11 #include <fstream> 12 #include <iostream> 13 #include <string> 14 #include <vector> 15 // [CLI11:public_includes:end] 16 17 #include "Error.hpp" 18 #include "StringTools.hpp" 19 20 namespace CLI { 21 // [CLI11:config_fwd_hpp:verbatim] 22 23 class App; 24 25 /// Holds values to load into Options 26 struct ConfigItem { 27 /// This is the list of parents 28 std::vector<std::string> parents{}; 29 30 /// This is the name 31 std::string name{}; 32 33 /// Listing of inputs 34 std::vector<std::string> inputs{}; 35 36 /// The list of parents and name joined by "." fullnameCLI::ConfigItem37 std::string fullname() const { 38 std::vector<std::string> tmp = parents; 39 tmp.emplace_back(name); 40 return detail::join(tmp, "."); 41 } 42 }; 43 44 /// This class provides a converter for configuration files. 45 class Config { 46 protected: 47 std::vector<ConfigItem> items{}; 48 49 public: 50 /// Convert an app into a configuration 51 virtual std::string to_config(const App *, bool, bool, std::string) const = 0; 52 53 /// Convert a configuration into an app 54 virtual std::vector<ConfigItem> from_config(std::istream &) const = 0; 55 56 /// Get a flag value to_flag(const ConfigItem & item) const57 virtual std::string to_flag(const ConfigItem &item) const { 58 if(item.inputs.size() == 1) { 59 return item.inputs.at(0); 60 } 61 throw ConversionError::TooManyInputsFlag(item.fullname()); 62 } 63 64 /// Parse a config file, throw an error (ParseError:ConfigParseError or FileError) on failure from_file(const std::string & name)65 std::vector<ConfigItem> from_file(const std::string &name) { 66 std::ifstream input{name}; 67 if(!input.good()) 68 throw FileError::Missing(name); 69 70 return from_config(input); 71 } 72 73 /// Virtual destructor 74 virtual ~Config() = default; 75 }; 76 77 /// This converter works with INI/TOML files; to write INI files use ConfigINI 78 class ConfigBase : public Config { 79 protected: 80 /// the character used for comments 81 char commentChar = '#'; 82 /// the character used to start an array '\0' is a default to not use 83 char arrayStart = '['; 84 /// the character used to end an array '\0' is a default to not use 85 char arrayEnd = ']'; 86 /// the character used to separate elements in an array 87 char arraySeparator = ','; 88 /// the character used separate the name from the value 89 char valueDelimiter = '='; 90 /// the character to use around strings 91 char stringQuote = '"'; 92 /// the character to use around single characters 93 char characterQuote = '\''; 94 /// the maximum number of layers to allow 95 uint8_t maximumLayers{255}; 96 /// the separator used to separator parent layers 97 char parentSeparatorChar{'.'}; 98 /// Specify the configuration index to use for arrayed sections 99 int16_t configIndex{-1}; 100 /// Specify the configuration section that should be used 101 std::string configSection{}; 102 103 public: 104 std::string 105 to_config(const App * /*app*/, bool default_also, bool write_description, std::string prefix) const override; 106 107 std::vector<ConfigItem> from_config(std::istream &input) const override; 108 /// Specify the configuration for comment characters comment(char cchar)109 ConfigBase *comment(char cchar) { 110 commentChar = cchar; 111 return this; 112 } 113 /// Specify the start and end characters for an array arrayBounds(char aStart,char aEnd)114 ConfigBase *arrayBounds(char aStart, char aEnd) { 115 arrayStart = aStart; 116 arrayEnd = aEnd; 117 return this; 118 } 119 /// Specify the delimiter character for an array arrayDelimiter(char aSep)120 ConfigBase *arrayDelimiter(char aSep) { 121 arraySeparator = aSep; 122 return this; 123 } 124 /// Specify the delimiter between a name and value valueSeparator(char vSep)125 ConfigBase *valueSeparator(char vSep) { 126 valueDelimiter = vSep; 127 return this; 128 } 129 /// Specify the quote characters used around strings and characters quoteCharacter(char qString,char qChar)130 ConfigBase *quoteCharacter(char qString, char qChar) { 131 stringQuote = qString; 132 characterQuote = qChar; 133 return this; 134 } 135 /// Specify the maximum number of parents maxLayers(uint8_t layers)136 ConfigBase *maxLayers(uint8_t layers) { 137 maximumLayers = layers; 138 return this; 139 } 140 /// Specify the separator to use for parent layers parentSeparator(char sep)141 ConfigBase *parentSeparator(char sep) { 142 parentSeparatorChar = sep; 143 return this; 144 } 145 /// get a reference to the configuration section sectionRef()146 std::string §ionRef() { return configSection; } 147 /// get the section section() const148 const std::string §ion() const { return configSection; } 149 /// specify a particular section of the configuration file to use section(const std::string & sectionName)150 ConfigBase *section(const std::string §ionName) { 151 configSection = sectionName; 152 return this; 153 } 154 155 /// get a reference to the configuration index indexRef()156 int16_t &indexRef() { return configIndex; } 157 /// get the section index index() const158 int16_t index() const { return configIndex; } 159 /// specify a particular index in the section to use (-1) for all sections to use index(int16_t sectionIndex)160 ConfigBase *index(int16_t sectionIndex) { 161 configIndex = sectionIndex; 162 return this; 163 } 164 }; 165 166 /// the default Config is the TOML file format 167 using ConfigTOML = ConfigBase; 168 169 /// ConfigINI generates a "standard" INI compliant output 170 class ConfigINI : public ConfigTOML { 171 172 public: ConfigINI()173 ConfigINI() { 174 commentChar = ';'; 175 arrayStart = '\0'; 176 arrayEnd = '\0'; 177 arraySeparator = ' '; 178 valueDelimiter = '='; 179 } 180 }; 181 // [CLI11:config_fwd_hpp:end] 182 } // namespace CLI 183