//********************************************************
// The following code example is taken from the book
//  C++23 - The Complete Guide
//  by Nicolai M. Josuttis (www.josuttis.com)
//  http://www.cppstd23.com
//
// The code is licensed under a
//  Creative Commons Attribution 4.0 International License
//  http://creativecommons.org/licenses/by/4.0/
//********************************************************


#include <iostream>
#include <print>
#include <string>
#include <expected>

template<typename ValT, typename ErrT>
void print(const std::expected<ValT,ErrT>& exp)
{
  if (exp) std::println("[value: {}]", *exp);
    else   std::println("[error: {}]", exp.error());
}

// convert string to int (might not be a valid int):
std::expected<int, std::string> asExpInt(const std::string& s)
{
  // is the string a valid int (at the beginning)?
  char first = s[0];
  if (first == '-') first = s[1];
  if (!std::isdigit(first)) {
    return std::unexpected("\"" + s + "\" is no valid int");
  }

  return std::stoi(s);
}

int main()
{
  auto square = [] (auto val) { return val * val; };  // unsafe: might overflow

  using ExpString = std::expected<std::string, std::string>;

  for (auto myExp : {ExpString{"42"}, ExpString{"data"}, ExpString{std::unexpected{"nodata"}}}) { 

    print(myExp);

    auto exp = myExp.or_else([] (const auto& err) {
                               std::println("ERROR: {}", err); 
                               return std::expected<std::string, std::string>{"fallback data"};
                             })
                    .and_then(asExpInt)
                    .or_else([] (const auto& err) {
                               std::println("ERROR: {}", err); 
                               return std::expected<int, std::string>{42};
                             });

    std::cout << "    => ";
    print(exp);

  }
}

