text: /*
   *  Copyright (c) 2016, Facebook, Inc.
   *  All rights reserved.
   *
   *  This source code is licensed under the BSD-style license found in the
   *  LICENSE file in the root directory of this source tree. An additional grant
   *  of patent rights can be found in the PATENTS file in the same directory.
   */
  
  #ifndef FATAL_INCLUDE_fatal_test_string_h
  #define FATAL_INCLUDE_fatal_test_string_h
  
  #include <fatal/string/string_view.h>
  #include <fatal/time/time.h>
  #include <fatal/type/tag.h>
  
  #include <chrono>
  #include <stdexcept>
  #include <string>
  #include <type_traits>
  
  #include <cstring>
  
  namespace fatal {
  
  inline void append(std::string &out, bool from) {
    out.append(from ? "true" : "false");
  }
  
  template <typename T, typename = decltype(std::to_string(std::declval<T>()))>
  void append(std::string &out, T from) {
    out.append(std::to_string(from));
  }
  
  template <typename R, typename P>
  void append(std::string &out, std::chrono::duration<R, P> from) {
    append(out, from.count());
    out.append(time::suffix(from));
  }
  
  inline void append(std::string &out, std::string const &from) {
    out.append(from);
  }
  
  inline void append(std::string &out, string_view from) {
    out.append(from.data(), from.size());
  }
  
  inline void append(std::string &out, char from) {
    out.push_back(from);
  }
  
  inline void append(std::string &out, char const *from) {
    out.append(from);
  }
  
  namespace detail {
  namespace string_impl {
  
  inline bool parse(tag<bool>, std::string const &from) {
    // TODO: use a compile-time trie??
    if (from == "true") {
      return true;
    }
  
    if (from == "false") {
      return false;
    }
  
    throw std::invalid_argument("unrecognized boolean");
  }
  
  inline short parse(tag<short>, std::string const &from) {
    return static_cast<short>(std::stoi(from));
  }
  
  inline int parse(tag<int>, std::string const &from) {
    return std::stoi(from);
  }
  
  inline long parse(tag<long>, std::string const &from) {
    return std::stol(from);
  }
  
  inline long long parse(tag<long long>, std::string const &from) {
    return std::stoll(from);
  }
  
  inline unsigned long parse(tag<unsigned long>, std::string const &from) {
    return std::stoul(from);
  }
  
  inline unsigned long long parse(
    tag<unsigned long long>, std::string const &from
  ) {
    return std::stoull(from);
  }
  
  inline float parse(tag<float>, std::string const &from) {
    return std::stof(from);
  }
  
  inline double parse(tag<double>, std::string const &from) {
    return std::stod(from);
  }
  
  inline long double parse(tag<long double>, std::string const &from) {
    return std::stold(from);
  }
  
  inline std::string parse(tag<std::string>, std::string const &from) {
    return from;
  }
  
  void to_string_impl(std::string &) {}
  
  template <typename T, typename... Args>
  void to_string_impl(std::string &out, T &&value, Args &&...args) {
    append(out, std::forward<T>(value));
  
    to_string_impl(out, std::forward<Args>(args)...);
  }
  
  } // namespace string_impl {
  } // namespace detail {
  
  // for internal tests only - no guaranteed efficiency
  // TODO: TEST
  template <typename To>
  To parse(std::string const &from) {
    return detail::string_impl::parse(tag<To>(), from);
  }
  
  // for internal tests only - no guaranteed efficiency
  // TODO: TEST
  template <typename... Args>
  std::string &append_to_string(std::string &out, Args &&...args) {
    detail::string_impl::to_string_impl(out, std::forward<Args>(args)...);
  
    return out;
  }
  
  // TODO: TEST
  template <typename... Args>
  std::string to_string(Args &&...args) {
    std::string out;
  
    append_to_string(out, std::forward<Args>(args)...);
  
    return out;
  }
  
  std::string const &to_string(std::string const &s) { return s; }
  std::string &to_string(std::string &s) { return s; }
  std::string &&to_string(std::string &&s) { return std::move(s); }
  
  } // namespace fatal {
  
  #endif // FATAL_INCLUDE_fatal_test_string_h
  