#!/usr/bin/env ruby
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# cd folly/futures/test && ruby then_compile_test.rb > ThenCompileTest.cpp

# An exercise in combinatorics.
# (ordinary/static function, member function, std::function, lambda)
# X
# returns (Future<R>, R)
# X
# accepts (Try<T>&&, Try<T> const&, Try<T>, T&&, T const&, T, nothing)

def test(form, *args)
  args = args.join(", ")
  [
  "{Future<B> f = someFuture<A>().#{form}(#{args});}",
  #"{Future<B> f = makeFuture(A()).then(#{args}, anExecutor);}",
  ]
end

def retval(ret)
  {
    "Future<B>" => "someFuture<B>()",
    "Try<B>" => "Try<B>(B())",
    "B" => "B()"
  }[ret]
end

return_types = [
  "Future<B>",
  "B",
  #"Try<B>",
]
try_param_types = [
    "Try<A>&&",
    "Try<A> const&",
    "Try<A>",
  ]
value_param_types = [
    "A&&",
    "A const&",
    "A",
  ]

def doMap(form, param_types, return_types)
  (
    return_types.map { |ret|
      param_types.map { |param|
        if param != "" then
          both = "#{ret}, #{param}"
          [
            ["&aFunction<#{both}>"],
            ["&SomeClass::aStaticMethod<#{both}>"],
            #["&SomeClass::aMethod<#{both}>", "&anObject"],
            ["aStdFunction<#{both}>()"],
            ["[&](#{param}){return #{retval(ret)};}"],
          ]
        else
          [["[&](){return #{retval(ret)};}"]]
        end
      }
    }.flatten(2)
  ).map {|a| test(form, a)}.flatten
end

def doMapMultiArg(form, param_types, return_types)
  (
    return_types.map { |ret|
      param_types.map { |param|
        if param != "" then
          both = "#{ret}, #{param}"
          [
            ["&SomeClass::aMethod<#{both}>", "&anObject"],
          ]
        else
          [["[&](){return #{retval(ret)};}"]]
        end
      }
    }.flatten(2)
  ).map {|a| test(form, a)}.flatten
end

# TODO(#T29171940): Modify this to thenTry on deletion of the Try form of then
tests = doMap("then", try_param_types, return_types)
tests.concat doMap("thenValue", value_param_types, return_types)
tests.concat doMapMultiArg("then", try_param_types, return_types)

print <<EOF
// This file is #{"@"}generated by then_compile_test.rb. Do not edit directly.

#include <folly/futures/test/ThenCompileTest.h>

using namespace folly;

TEST(Basic, thenVariants) {
  SomeClass anObject;

  #{tests.join("\n  ")}
}
EOF
