text: #pragma once
  
  // Distributed under the 3-Clause BSD License.  See accompanying
  // file LICENSE or https://github.com/CLIUtils/CLI11 for details.
  
  #include <algorithm>
  #include <fstream>
  #include <iostream>
  #include <string>
  
  #include "CLI/Error.hpp"
  #include "CLI/StringTools.hpp"
  
  namespace CLI {
  
  class App;
  
  /// Holds values to load into Options
  struct ConfigItem {
      /// This is the list of parents
      std::vector<std::string> parents{};
  
      /// This is the name
      std::string name{};
  
      /// Listing of inputs
      std::vector<std::string> inputs{};
  
      /// The list of parents and name joined by "."
      std::string fullname() const {
          std::vector<std::string> tmp = parents;
          tmp.emplace_back(name);
          return detail::join(tmp, ".");
      }
  };
  
  /// This class provides a converter for configuration files.
  class Config {
    protected:
      std::vector<ConfigItem> items{};
  
    public:
      /// Convert an app into a configuration
      virtual std::string to_config(const App *, bool, bool, std::string) const = 0;
  
      /// Convert a configuration into an app
      virtual std::vector<ConfigItem> from_config(std::istream &) const = 0;
  
      /// Get a flag value
      virtual std::string to_flag(const ConfigItem &item) const {
          if(item.inputs.size() == 1) {
              return item.inputs.at(0);
          }
          throw ConversionError::TooManyInputsFlag(item.fullname());
      }
  
      /// Parse a config file, throw an error (ParseError:ConfigParseError or FileError) on failure
      std::vector<ConfigItem> from_file(const std::string &name) {
          std::ifstream input{name};
          if(!input.good())
              throw FileError::Missing(name);
  
          return from_config(input);
      }
  
      /// Virtual destructor
      virtual ~Config() = default;
  };
  
  /// This converter works with INI/TOML files; to write proper TOML files use ConfigTOML
  class ConfigBase : public Config {
    protected:
      /// the character used for comments
      char commentChar = ';';
      /// the character used to start an array '\0' is a default to not use
      char arrayStart = '\0';
      /// the character used to end an array '\0' is a default to not use
      char arrayEnd = '\0';
      /// the character used to separate elements in an array
      char arraySeparator = ' ';
      /// the character used separate the name from the value
      char valueDelimiter = '=';
  
    public:
      std::string
      to_config(const App * /*app*/, bool default_also, bool write_description, std::string prefix) const override;
  
      std::vector<ConfigItem> from_config(std::istream &input) const override;
      /// Specify the configuration for comment characters
      ConfigBase *comment(char cchar) {
          commentChar = cchar;
          return this;
      }
      /// Specify the start and end characters for an array
      ConfigBase *arrayBounds(char aStart, char aEnd) {
          arrayStart = aStart;
          arrayEnd = aEnd;
          return this;
      }
      /// Specify the delimiter character for an array
      ConfigBase *arrayDelimiter(char aSep) {
          arraySeparator = aSep;
          return this;
      }
      /// Specify the delimiter between a name and value
      ConfigBase *valueSeparator(char vSep) {
          valueDelimiter = vSep;
          return this;
      }
  };
  
  /// the default Config is the INI file format
  using ConfigINI = ConfigBase;
  
  /// ConfigTOML generates a TOML compliant output
  class ConfigTOML : public ConfigINI {
  
    public:
      ConfigTOML() {
          commentChar = '#';
          arrayStart = '[';
          arrayEnd = ']';
          arraySeparator = ',';
          valueDelimiter = '=';
      }
  };
  } // namespace CLI
  