/*
 * 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.
 */

#ifndef THRIFT_FATAL_REFLECTION_H_
#define THRIFT_FATAL_REFLECTION_H_ 1

#include <cstdint>
#include <string_view>
#include <type_traits>
#include <utility>

#include <fatal/type/conditional.h>
#include <fatal/type/convert.h>
#include <fatal/type/data_member_getter.h>
#include <fatal/type/enum.h>
#include <fatal/type/foreach.h>
#include <fatal/type/get.h>
#include <fatal/type/get_type.h>
#include <fatal/type/list.h>
#include <fatal/type/pair.h>
#include <fatal/type/registry.h>
#include <fatal/type/search.h>
#include <fatal/type/traits.h>
#include <fatal/type/transform.h>
#include <fatal/type/variant_traits.h>
#include <folly/CppAttributes.h>
#include <folly/Traits.h>
#include <folly/functional/Invoke.h>
#include <thrift/lib/cpp2/FieldRef.h>
#include <thrift/lib/cpp2/FieldRefTraits.h>
#include <thrift/lib/cpp2/TypeClass.h>

#include <thrift/lib/cpp2/reflection/internal/reflection-inl-pre.h>

namespace apache::thrift {

/**
 * READ ME FIRST: this file is divided into sections for each specific
 * reflection API.
 *
 * To quickly navigate this file, look for the string "SECTION: " without the
 * quotes.
 *
 * Note that the compile-time reflection API depends on metadata that's
 * generated by the Thrift compiler. In order to have this metadata available,
 * there are three simple but necessary steps:
 *
 *  - enable code generation for `cpp2` as the target language;
 *  - enable `reflection` as one of the `thrift_cpp2_options` flags;
 *  - include the appropriate file containing the metadata (say your module is
 *    defined in `some_dir/Module.thrift`, corresponding files can be included
 *    from `some_dir/gen-cpp2/`):
 *
 *      - Module_fatal.h: general module metadata
 *      - Module_fatal_struct.h: metadata for all structures
 *      - Module_fatal_union.h: metadata for all variants (Thrift unions)
 *      - Module_fatal_types.h: convenience header that includes metadata for
 *        modules, structures, and variants
 *      - Module_fatal_all.h: convenience header that includes all the
 *        metadata generated by the Thrift compiler
 *
 * Thrift breaks the metadata up in several files to help reduce compilation
 * times by including only what's needed.
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */

//////////////////////////////////////////////
// SECTION: IMPORTANT NOTES AND CONVENTIONS //
//////////////////////////////////////////////

/**
 * NOTE ON COMPILE-TIME STRINGS: many strings found in the Thrift file are
 * converted to compile-time strings in the form of a `fatal::sequence`
 * of `char`.
 *
 * They are often represented as general C++ identifiers. Not all strings are
 * directly representable as C++ identifiers though, given that not all
 * characters are accepted as identifier names, only [_a-zA-Z0-9]. When that's
 * the case, the invalid characters are replaced by an underscode (_).
 *
 * Names starting with numbers are prefixed with 's_'. For example, the string
 * "42 is it" could be represented by the identifier 's_42_is_it'.
 *
 * Collisions are solved by appending a positive integer starting at 1 and
 * growing by 1 per collision, in the order the identifiers appear. For
 * instance, say there are three strings "a_", "a " and "a.". "a_" could be
 * represented by the identifier 'a_' while "a " could be represented by 'a_1'
 * and "a." could be 'a_2'.
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */

////////////////////////////////////////////
// SECTION: TYPE ALIASES AND ENUMERATIONS //
////////////////////////////////////////////

/**
 * An alias to the type used by Thrift as a struct's field ID.
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
using field_id_t = std::int16_t;

/**
 * An alias to the type used by Thrift as a type's unique identifier.
 *
 * NOTE: this is a legacy feature and should be avoided on new code.
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
using legacy_type_id_t = std::uint64_t;

/**
 * Represents whether a field is required to be set in a given structure or not.
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
enum class optionality {
  /**
   * Field is required.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  required,
  /**
   * Field is optional.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  optional,
  /**
   * Field is optional on the reading side but required on the writing side.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  required_of_writer,
  /**
   * Field has terse write semantics.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  terse,
};

/////////////////////////////
// SECTION: TYPE CLASS API //
/////////////////////////////

/**
 * Returns the type class of a thrift class, which is either a structure, a
 * variant, or an exception.
 *
 * Example:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  struct MyStruct {
 *    1: i32 a
 *    2: string b
 *    3: double c
 *  }
 *
 *  union MyUnion {
 *    1: i32 a
 *    2: string b
 *    3: double c
 *  }
 *
 *  enum MyEnum { a, b, c }
 *
 *  typedef list<string> MyList;
 *
 *  /////////////
 *  // foo.cpp //
 *  /////////////
 *
 *  // yields `type_class::structure`
 *  using result1 = reflect_type_class_of_thrift_class<MyStruct>;
 *
 *  // yields `type_class::variant`
 *  using result1 = reflect_type_class_of_thrift_class<MyUnion>;
 *
 *  // yields `type_class::unknown`
 *  using result2 = reflect_type_class_of_thrift_class<MyEnum>;
 *
 *  // yields `type_class::unknown`
 *  using result3 = reflect_type_class_of_thrift_class<MyList>;
 *
 *  // yields `type_class::unknown`
 *  using result4 = reflect_type_class_of_thrift_class<void>;
 */
template <typename T>
using reflect_type_class_of_thrift_class =
    typename detail::reflect_type_class_of_thrift_class_impl<T>::type;

/////////////////////////
// SECTION: MODULE API //
/////////////////////////

/**
 * Holds reflection metadata for stuff defined in a Thrift file.
 *
 * NOTE: this class template is only intended to be instantiated by Thrift.
 * Users should ignore the template parameters taken by it and focus simply on
 * the members provided.
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename Traits>
struct reflected_module {
  /**
   * The name.
   *
   * A `fatal::constant_sequence` (compile-time string) representing the module
   * name.
   *
   * Example:
   *
   *  // MyModule.thrift
   *
   *  namespace cpp2 My.Namespace
   *  struct MyStruct {}
   *
   *  // C++
   *
   *  using info = reflect_module<My::Namespace::MyModule_tags::module>;
   *  using name = typename info::name;
   *  // yields `fatal::constant_sequence<
   *  //   char, 'M', 'y', 'M', 'o', 'd', 'u', 'l', 'e',
   *  // >`
   */
  using name = typename Traits::name;

  /**
   * The map from language to namespace.
   *
   * A `fatal::list` of `fatal::pair` where the key is a `fatal::sequence`
   * (compile-time string) representing the language, associated with a
   * compile-time string representing the namespace.
   *
   * Example:
   *
   *  // MyModule.thrift
   *
   *  namespace cpp My.NamespaceCpp
   *  namespace cpp2 My.Namespace
   *  namespace java My.Namespace
   *
   *  struct MyStruct {
   *    1: i32 a
   *    2: string b
   *    3: double c
   *  }
   *
   *  // C++
   *
   *  using info = reflect_module<My::Namespace::MyModule_tags::module>;
   *
   *  FATAL_S(cpp, "cpp");
   *  FATAL_S(cpp2, "cpp2");
   *  FATAL_S(java, "java");
   *
   *  // yields `fatal::sequence<
   *  //   char,
   *  //   'M', 'y', ':', ':', 'N', 'a', 'm', 'e',
   *  //   's', 'p', 'a', 'c', 'e', 'C', 'p', 'p'
   *  // >`
   *  using result1 = fatal::get<info::namespaces, cpp>;
   *
   *  // yields `fatal::sequence<
   *  //   char, 'M', 'y', ':', ':', 'N', 'a', 'm', 'e', 's', 'p', 'a', 'c', 'e'
   *  // >`
   *  using result2 = fatal::get<info::namespaces, cpp2>;
   *
   *  // yields `fatal::sequence<
   *  //   char, 'M', 'y', '.', 'N', 'a', 'm', 'e', 's', 'p', 'a', 'c', 'e'
   *  // >`
   *  using result3 = fatal::get<info::namespaces, java>;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using namespaces = typename Traits::namespaces;

  /**
   * The list of enumerations with reflection metadata available.
   *
   * A `fatal::list` of `fatal::pair` where the first type is the one generated
   * by the Thrift compiler for each enumeration, and the second one is the
   * compile-time string (`fatal::sequence`) representing the enumeration name.
   *
   * Use `fatal::enum_traits` to retrieve reflection information for each
   * enumeration (fatal/type/enum.h).
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using enums = typename Traits::enums;

  /**
   * The list of unions with reflection metadata available.
   *
   * A `fatal::list` of `fatal::pair` where the first type is the one generated
   * by the Thrift compiler for each union, and the second one is the
   * compile-time string (`fatal::sequence`) representing the union name.
   *
   * Use `fatal::variant_traits` to retrieve reflection information for each
   * union (fatal/type/variant_traits.h).
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using unions = typename Traits::unions;

  /**
   * The list of structs with reflection metadata available.
   *
   * A `fatal::list` of `fatal::pair` where the first type is the one generated
   * by the Thrift compiler for each struct, and the second one is the
   * compile-time string (`fatal::sequence`) representing the struct name.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using structs = typename Traits::structs;

  /**
   * The list of services with reflection metadata available.
   *
   * A `fatal::list` of compile-time strings
   * (`fatal::sequence`) representing each service name.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using services = typename Traits::services;
};

/**
 * Retrieves reflection metadata (as a `reflected_module`) associated with the
 * given reflection metadata tag.
 *
 * The Thrift compiler generates a reflection metadata tag for each Thrift file
 * named `namespace::thriftfilename_tags::module`.
 *
 * If the given tag does not represent a Thrift module, or if there's no
 * reflection metadata available for it, compilation will fail.
 *
 * See the documentation on `reflected_module` (above) for more information on
 * the returned type.
 *
 * Example:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  enum MyEnum1 { a, b, c }
 *  enum MyEnum2 { x, y, x }
 *
 *  //////////////////
 *  // whatever.cpp //
 *  //////////////////
 *  using info = reflect_module<My::Namespace::MyModule_tags::module>;
 *
 *  // yields `2`
 *  auto result1 = info::enums::size;
 *
 *  // fails compilation
 *  using result2 = reflect_module<void>;
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename Tag>
using reflect_module = fatal::
    registry_lookup<apache::thrift::detail::reflection_metadata_tag, Tag>;

/**
 * Retrieves reflection metadata (as a `reflected_module`) associated with the
 * given reflection metadata tag.
 *
 * The Thrift compiler generates a reflection metadata tag for each Thrift file
 * named `namespace::thriftfilename_tags::module`.
 *
 * If the given tag does not represent a Thrift module, or if there's no
 * reflection metadata available for it, `Default` will be returned.s If
 * `Default` is not specified, it defaults to void.
 *
 * See the documentation on `reflected_module` (above) for more information on
 * the returned type.
 *
 * Example:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  enum MyEnum1 { a, b, c }
 *  enum MyEnum2 { x, y, x }
 *
 *  //////////////////
 *  // whatever.cpp //
 *  //////////////////
 *  using info = try_reflect_module<
 *    My::Namespace::MyModule_tags::module,
 *    void
 *  >;
 *
 *  // yields `2`
 *  auto result1 = info::enums::size;
 *
 *  // yields `void`
 *  using result2 = try_reflect_module<int>;
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename Tag, typename Default = void>
using try_reflect_module = fatal::try_registry_lookup<
    apache::thrift::detail::reflection_metadata_tag,
    Tag,
    Default>;

/**
 * Tells whether the given type is a tag that represents the reflection metadata
 * of the types declared in a Thrift file.
 *
 * Example:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  struct MyStruct {
 *    1: i32 a
 *    2: string b
 *    3: double c
 *  }
 *
 *  /////////////
 *  // foo.cpp //
 *  /////////////
 *
 *  // yields `std::true_type`
 *  using result1 = is_reflectable_module<MyModule_tags::module>;
 *
 *  // yields `std::false_type`
 *  using result2 = is_reflectable_module<MyStruct>;
 *
 *  // yields `std::false_type`
 *  using result3 = is_reflectable_module<void>;
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename T>
using is_reflectable_module = std::
    integral_constant<bool, !std::is_same_v<try_reflect_module<T, void>, void>>;

/**
 * Gets the reflection metadata tag for the Thrift file where the type `T` is
 * declared.
 *
 * The type `T` must be either a struct, enum or union.
 *
 * Example:
 *
 *  // MyModule.thrift
 *
 *  namespace cpp2 My.Namespace
 *
 *  struct MyStruct {
 *    1: i32 a
 *    2: string b
 *    3: double c
 *  }
 *
 *  enum MyEnum { a, b, c }
 *
 *  // C++
 *
 *  // yields `My::Namespace::MyModule_tags::module`
 *  using result1 = reflect_module_tag<MyStruct>;
 *
 *  // yields `My::Namespace::MyModule_tags::module`
 *  using result2 = reflect_module_tag<MyEnum>;
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename T>
using reflect_module_tag =
    typename apache::thrift::detail::reflect_module_tag_selector<
        reflect_type_class_of_thrift_class<T>,
        T,
        false>::type;

/**
 * Tries to get the reflection metadata tag for the Thrift file where the type
 * `T` is declared.
 *
e:
 *
 *  // MyModule.thrift
 *
 *  namespace cpp2 My.Namespace
 *
 *  struct MyStruct {
 *    1: i32 a
 *    2: string b
 *    3: double c
 *  }
 *
 *  enum MyEnum { a, b, c }
 *
 *  // C++
 *
 *  // yields `My::Namespace::MyModule_tags::module`
 *  using result1 = reflect_module_tag<MyStruct, void>;
 *
 *  // yields `My::Namespace::MyModule_tags::module`
 *  using result2 = reflect_module_tag<MyEnum, void>;
 *
 *  // yields `void`
 *  using result3 = reflect_module_tag<int, void>;
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename T, typename Default = void>
using try_reflect_module_tag =
    typename apache::thrift::detail::reflect_module_tag_selector<
        reflect_type_class_of_thrift_class<T>,
        T,
        true,
        Default>::type;

/**
 * Represents an annotation from `reflected_annotations::map`.
 *
 * For the examples below, consider code generated for this Thrift file:
 *
 *  struct Foo {
 *    1: i32 z
 *  } (
 *    a = '{not a valid format}',
 *    b = '{"valid": "format", "foo": 10, "bar": true, "x": [-5, 0, 5]}',
 *    c = '"hello"'
 *  }
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename Key, typename Value>
struct annotation {
  /**
   * Represents the annotation key as a compile-time string, in the form of a
   * `fatal::sequence` of type `char`.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using key = Key;

  /**
   * Represents the annotation value as a compile-time string, in the form of a
   * `fatal::sequence` of type `char`.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using value = Value;
};

/**
 * Holds reflection metadata for annotations.
 *
 * For the examples below, consider code generated for this Thrift file:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  struct MyStruct {
 *    1: i32 a
 *    2: string b
 *    3: double c
 *  } (
 *    some.annotation = "some value",
 *    another.annotation = "another value",
 *  )
 *
 * NOTE: this class template is only intended to be instantiated by Thrift.
 * Users should ignore the template parameters taken by it and focus simply on
 * the members provided.
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename Metadata>
struct reflected_annotations {
  /**
   * An implementation defined type that provides the names for each annotation
   * key as a member type alias named after the key.
   *
   * These type aliases are used as the key for the `map` member.
   *
   * Look for 'NOTE ON COMPILE-TIME STRINGS' for how the strings are converted
   * to C++ identifiers - caveat: instead of using the order they appear as the
   * precedence for collision resolution, it uses lexicographical order of the
   * keys.
   *
   * Example:
   *
   *  using annotations = reflect_struct<MyStruct>::annotations;
   *
   *  // yields `fatal::sequence<char,
   *  //   's', 'o', 'm', 'e', '.',
   *  //   'a', 'n', 'n', 'o', 't', 'a', 't', 'i', 'o', 'n'
   *  // >`
   *  using result1 = annotations::keys::some_annotation;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using keys = typename Metadata::keys;

  /**
   * An implementation defined type that provides the names for each annotation
   * value as a member type alias named after the key.
   *
   * These type aliases are used as the value for the `map` member.
   *
   * Look for 'NOTE ON COMPILE-TIME STRINGS' for how the strings are converted
   * to C++ identifiers - caveat: instead of using the order they appear as the
   * precedence for collision resolution, it uses lexicographical order of the
   * keys.
   *
   * Example:
   *
   *  using annotations = reflect_struct<MyStruct>::annotations;
   *
   *  // yields `fatal::sequence<char,
   *  //   's', 'o', 'm', 'e', ' ', 'v', 'a', 'l', 'u', 'e'
   *  // >`
   *  using result1 = annotations::values::some_annotation;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using values = typename Metadata::values;

  /**
   * A list of `annotation` representing the annotations declared in the Thrift
   * file, sorted by keys.
   *
   * See the `annotation` type for more information.
   *
   * Example:
   *
   *  // yields an instantiation of the `reflected_annotations` template
   *  using annotations = reflect_struct<MyStruct>::annotations;
   *
   *  FATAL_S(key, "another.annotation");
   *
   *  // yields `fatal::sequence<char,
   *  //   'a', 'n', 'o', 't', 'h', 'e', 'r', ' ', 'v', 'a', 'l', 'u', 'e'
   *  // >`
   *  using result1 = fatal::get<annotations::map, key>::value;
   *
   *  // yields `fatal::sequence<char,
   *  //   's', 'o', 'm', 'e', ' ', 'v', 'a', 'l', 'u', 'e'
   *  // >`
   *  using result2 = fatal::get<
   *    annotations::map,
   *    annotations::keys::some_annotation
   *  >::value;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using map = typename Metadata::map;
};

////////////////////////////
// SECTION: STRUCTURE API //
////////////////////////////

/**
 * Holds reflection metadata for a given struct.
 *
 * NOTE: this class template is only intended to be instantiated by Thrift.
 * Users should ignore the template parameters taken by it and focus simply on
 * the members provided.
 *
 * For the examples below, consider code generated for this Thrift file:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  struct MyStruct {
 *    1: i32 a
 *    2: string b
 *    3: double c (
 *      member.note = "member text",
 *      another.member.note = "another member text",
 *    )
 *  } (
 *    some.annotation = "some value",
 *    another.annotation = "another value",
 *  )
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename Traits>
struct reflected_struct {
  /**
   * A type alias for the struct itself.
   *
   * Example:
   *
   *  using info = reflect_struct<MyStruct>;
   *
   *  // yields `MyStruct`
   *  using result = info::type;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using type = typename Traits::type;

  /**
   * A compile-time string representing the struct name.
   *
   * Example:
   *
   *  using info = reflect_struct<MyStruct>;
   *
   *  // yields `fatal::sequence<
   *  //   char,
   *  //   'M', 'y', 'S', 't', 'r', 'u', 'c', 't'
   *  // >`
   *  using result = info::name;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using name = typename Traits::name;

  /**
   * The reflection metadata tag for the Thrift file where this structure is
   * declared.
   *
   * Example:
   *
   *  using info = reflect_struct<MyStruct>;
   *
   *  // yields `My::Namespace::MyModule_tags::module`
   *  using result = info::module;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using module = typename Traits::metadata::module;

  /**
   * An implementation defined type template that provides the appropriate
   * `reflected_struct_data_member` for each data member as a member type alias
   * with the same name.
   *
   * These type aliases are used as the type mapped by the `members` member
   * offered by the `reflected_struct` class.
   *
   * An optional transform can be specified, which will be applied on top of the
   * members' `reflected_struct_data_member`.
   *
   * Example:
   *
   *  using info = reflect_struct<MyStruct>;
   *
   *  // yields `fatal::sequence<char, 'a'>`
   *  using result1 = info::member::a::name;
   *
   *  // yields `std::int32_t`
   *  using result2 = info::member::a::type;
   *
   *  // yields `1`
   *  using result3 = info::member::a::id::value;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using member = typename Traits::member;

  /**
   * A `fatal::list` of `reflected_struct_data_member` representing each member.
   *
   * See the documentation for `reflected_struct_data_member` (below) for more
   * information on its members.
   *
   * Example:
   *
   *  struct visitor {
   *    template <typename MemberInfo>
   *    void operator ()(fatal::tag<MemberInfo>) {
   *      using name = typename MemberInfo::name;
   *      std::cout << "- member: " << fatal::z_data<name>() << '\n';
   *    }
   *  };
   *
   *  using info = reflect_struct<MyStruct>;
   *
   *  // prints the names of all members of `MyStruct`
   *  fatal::foreach<info::members>(visitor());
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using members = typename Traits::members;

  /**
   * An instantiation of `reflected_annotations` representing the annotations
   * declared for this type in the Thrift file.
   *
   * Example:
   *
   *  using info = reflect_struct<MyStruct>;
   *
   *  // yields `fatal::sequence<char,
   *  //   'a', 'n', 'o', 't', 'h', 'e', 'r', ' ', 'v', 'a', 'l', 'u', 'e'
   *  // >`
   *  using result = info::annotations::values::another_annotation;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using annotations = typename Traits::metadata::annotations;

  /**
   * An implementation defined type that provides the annotations for each
   * member of the struct. Each member's annotations are represented by an
   * instance of `reflected_annotations` named after the member itself.
   *
   * Example:
   *
   *  using info = reflect_struct<MyStruct>;
   *
   *  // yields `fatal::sequence<char,
   *  //   'm', 'e', 'm', 'b', 'e', 'r', ' ', 't', 'e', 'x', 't'
   *  // >`
   *  using result1 = info::members_annotations::c::values::member_note
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using members_annotations = typename Traits::members_annotations;

  /**
   * A unique identifier generated by thrift for this structure.
   *
   * NOTE: this is a legacy feature and should be avoided on new code.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using legacy_id = typename Traits::metadata::legacy_id;
};

/**
 * Holds reflection metadata for a given struct's data member.
 *
 * NOTE: this class template is only intended to be instantiated by Thrift.
 * Users should ignore the template parameters taken by it and focus simply on
 * the members provided.
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename Traits>
struct reflected_struct_data_member {
  /**
   * A `fatal::sequence` of `char` representing the data member name as
   * a compile-time string.
   *
   * Example:
   *
   *  // MyModule.thrift
   *
   *  namespace cpp2 My.Namespace
   *
   *  struct MyStruct {
   *    1: i32 fieldA
   *    2: string fieldB
   *    3: double fieldC
   *  }
   *
   *  // C++
   *
   *  using info = reflect_struct<MyStruct>;
   *  using member = info::member::fieldC;
   *
   *  // yields `fatal::sequence<char, 'f', 'i', 'e', 'l', 'd', 'C'>`
   *  using result1 = member::name;
   *
   *  // yields "fieldC"
   *  auto result2 = fatal::z_data<result1>();
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using name = typename Traits::name;

  /**
   * The type of the data member.
   *
   * Example:
   *
   *  // MyModule.thrift
   *
   *  namespace cpp2 My.Namespace
   *
   *  struct MyStruct {
   *    1: i32 fieldA
   *    2: string fieldB
   *    3: double fieldC
   *  }
   *
   *  // C++
   *
   *  using info = reflect_struct<MyStruct>;
   *  using member = info::member::fieldC;
   *
   *  // yields `double`
   *  using result1 = member::type;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using type = typename Traits::type;

  /**
   * A tag type representing the name of this member.
   */
  using tag = typename Traits::tag;

  /**
   * A `std::integral_constant` of type `field_id_t` representing the Thrift
   * field id for the data member.
   *
   * Example:
   *
   *  // MyModule.thrift
   *
   *  namespace cpp2 My.Namespace
   *
   *  struct MyStruct {
   *    1: i32 fieldA
   *    2: string fieldB
   *    3: double fieldC
   *  }
   *
   *  // C++
   *
   *  using info = reflect_struct<MyStruct>;
   *  using member = info::member::fieldC;
   *
   *  // yields `std::integral_constant<field_id_t, 3>`
   *  using result1 = member::id;
   *
   *  // yields `3`
   *  auto result2 = result1::value;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using id = std::integral_constant<field_id_t, Traits::id>;

  /**
   * A `std::integral_constant` of type `optionality` representing whether a
   * field is qualified as required or optional.
   *
   * Example:
   *
   *  // MyModule.thrift
   *
   *  namespace cpp2 My.Namespace
   *
   *  struct MyStruct {
   *    1: required i32 fieldA
   *    2: optional string fieldB
   *    3: double fieldC
   *  }
   *
   *  // C++
   *
   *  using info = reflect_struct<MyStruct>;
   *  using a = info::member::fieldA;
   *  using b = info::member::fieldB;
   *  using c = info::member::fieldC;
   *
   *  // yields `std::integral_constant<optionality, optionality::required>`
   *  using result1 = a::required;
   *
   *  // yields `std::integral_constant<optionality, optionality::optional>`
   *  using result2 = b::required;
   *
   *  // yields `std::integral_constant<
   *  //   optionality,
   *  //   optionality::required_of_writer
   *  // >`
   *  using result3 = c::required;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using optional = std::integral_constant<optionality, Traits::optional>;

  /**
   * A type that works as a getter for the data member.
   *
   * See also Fatal's documentation on `FATAL_DATA_MEMBER_GETTER` in
   * `fatal/type/traits.h` for more information on how to make the most out of
   * the data member getter.
   *
   * Example:
   *
   *  // MyModule.thrift
   *
   *  namespace cpp2 My.Namespace
   *
   *  struct MyStruct {
   *    1: i32 fieldA
   *    2: string fieldB
   *    3: double fieldC
   *  }
   *
   *  // C++
   *
   *  using info = reflect_struct<MyStruct>;
   *  using member = info::member::fieldC;
   *  using getter = member::getter;
   *
   *  MyStruct pod;
   *
   *  pod.fieldC = 7.2;
   *
   *  // yields `7.2`
   *  auto result1 = getter{}(pod);
   *
   *  // sets  `5.6` on `pod.fieldC`
   *  getter{}(pod) = 5.6;
   *
   *  // yields `5.6`
   *  auto result2 = pod.fieldC;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using getter = typename Traits::getter;

  /**
   * Similar to getter, but return `(optional_)?field_ref` instead.
   */
  using field_ref_getter = typename Traits::field_ref_getter;

  /**
   * The type class for this member.
   *
   * Example:
   *
   *  // MyModule.thrift
   *
   *  namespace cpp2 My.Namespace
   *
   *  struct MyStruct {
   *    1: i32 fieldA
   *    2: string fieldB
   *    3: double fieldC
   *  }
   *
   *  // C++
   *
   *  using info = reflect_struct<MyStruct>;
   *  using member = info::member::fieldC;
   *
   *  // yields `type_class::floating_point`
   *  using result1 = member::type_class;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using type_class = typename Traits::type_class;

  /**
   * An instantiation of `reflected_annotations` representing the annotations
   * declared for this member in the Thrift file.
   *
   * Example:
   *
   *  // MyModule.thrift
   *
   *  namespace cpp2 My.Namespace
   *
   *  struct MyStruct {
   *    1: i32 fieldA
   *    2: string fieldB
   *    3: double fieldC (field.note = "some notes")
   *  }
   *
   *  // MyModule.cpp
   *
   *  using info = reflect_struct<MyStruct>;
   *  using member = info::member::fieldC;
   *
   *  // yields `fatal::sequence<char,
   *  //   's', 'o', 'm', 'e', ' ', 'n', 'o', 't', 'e', 's'
   *  // >`
   *  using result = member::annotations::values::field_note;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using annotations = typename Traits::annotations;

  /**
   * Checks whether the member represented by this metadata is set in the given
   * object.
   *
   * Example:
   *
   *  // MyModule.thrift
   *
   *  namespace cpp2 My.Namespace
   *
   *  struct MyStruct {
   *    1: optional i32 field
   *  }
   *
   *  // MyModule.cpp
   *
   *  using info = reflect_struct<MyStruct>;
   *  using member = info::member::field;
   *
   *  MyStruct pod;
   *
   *  // yields `false`
   *  bool result1 = member::is_set(pod);
   *
   *  pod.set_field(42);
   *
   *  // yields `true`
   *  bool result2 = member::is_set(pod);
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  template <typename T>
  static constexpr inline bool is_set(T const& owner) {
    namespace impl = apache::thrift::detail::reflection_impl;
    return impl::is_set(field_ref_getter{}(owner));
  }

  /**
   * Marks the member as being either set or not set on the parent object.
   *
   * Example:
   *
   *  // MyModule.thrift
   *
   *  namespace cpp2 My.Namespace
   *
   *  struct MyStruct {
   *    1: optional i32 field
   *  }
   *
   *  // MyModule.cpp
   *
   *  using info = reflect_struct<MyStruct>;
   *  using member = info::types::members<info::member::field::name>;
   *
   * MyStruct pod;
   *
   * // mark `field` as being set
   * member::mark_set(pod, true)
   *
   *
   * @author: Dylan Knutson <dymk@fb.com>
   */
  template <typename T>
  static constexpr inline bool mark_set(T& owner, bool set) {
    namespace impl = apache::thrift::detail::reflection_impl;
    impl::mark_set(field_ref_getter{}(owner), set);
    return set;
  }
};

/**
 * Retrieves reflection metadata (as a `reflected_struct`) associated with the
 * given struct.
 *
 * If the given type is not a Thrift struct, or if there's no reflection
 * metadata available for it, compilation will fail.
 *
 * See the documentation on `reflected_struct` (above) for more information on
 * the returned type.
 *
 * Example:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  struct MyStruct {
 *    1: i32 a
 *    2: string b
 *    3: double c
 *  }
 *
 *  //////////////////
 *  // whatever.cpp //
 *  //////////////////
 *  using info = reflect_struct<My::Namespace::MyStruct>;
 *
 *  // yields `3`
 *  auto result = fatal::size<info::members>::value;
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename Struct>
using reflect_struct = fatal::
    registry_lookup<apache::thrift::detail::struct_traits_metadata_tag, Struct>;

/**
 * Retrieves reflection metadata (as a `reflected_struct`) associated with the
 * given struct.
 *
 * If the given type is not a Thrift struct, or if there's no reflection
 * metadata available for it, `Default` will be returned. If `Default` is not
 * specified, it defaults to void.
 *
 * See the documentation on `reflected_struct` (above) for more information on
 * the returned type.
 *
 * Example:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  struct MyStruct {
 *    1: i32 a
 *    2: string b
 *    3: double c
 *  }
 *
 *  //////////////////
 *  // whatever.cpp //
 *  //////////////////
 *  using info = reflect_struct<My::Namespace::MyStruct>;
 *
 *  // yields `3`
 *  auto result = fatal::size<info::members>::value;
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename Struct, typename Default = void>
using try_reflect_struct = fatal::try_registry_lookup<
    apache::thrift::detail::struct_traits_metadata_tag,
    Struct,
    Default>;

/**
 * Tells whether the given type is a Thrift struct with compile-time reflection
 * support.
 *
 * Example:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  struct MyStruct {
 *    1: i32 a
 *    2: string b
 *    3: double c
 *  }
 *
 *  enum MyEnum { a, b, c }
 *
 *  /////////////
 *  // foo.cpp //
 *  /////////////
 *
 *  // yields `std::true_type`
 *  using result1 = is_reflectable_struct<MyStruct>;
 *
 *  // yields `std::false_type`
 *  using result2 = is_reflectable_struct<MyEnum>;
 *
 *  // yields `std::false_type`
 *  using result3 = is_reflectable_struct<void>;
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename T>
using is_reflectable_struct = std::
    integral_constant<bool, !std::is_same_v<try_reflect_struct<T, void>, void>>;

//////////////////////////////
// SECTION: ENUMERATION API //
//////////////////////////////

/**
 * Holds reflection metadata for a given enumeration.
 *
 * NOTE: this class template is only intended to be instantiated by Thrift.
 * Users should ignore the template parameters taken by it and focus simply on
 * the members provided.
 *
 * For the examples below, consider code generated for this Thrift file:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  enum MyEnum {
 *    a, b, c
 *  } (
 *    some.annotation = "some value",
 *    another.annotation = "another value",
 *  )
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename T>
struct reflected_enum {};

/**
 * Retrieves reflection metadata (as a `reflected_enum`) associated with the
 * given enumeration.
 *
 * If the given type is not a Thrift enumeration, or if there's no reflection
 * metadata available for it, compilation will fail.
 *
 * See the documentation on `reflected_enum` (above) for more information on
 * the returned type.
 *
 * Example:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  enum MyEnum { a, b, c }
 *
 *  //////////////////
 *  // whatever.cpp //
 *  //////////////////
 *  using info = reflect_enum<My::Namespace::MyEnum>;
 *
 *  // yields `MyEnum`
 *  auto result = info::type;
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename T>
using reflect_enum = reflected_enum<T>;

/**
 * Tells whether the given type is a Thrift enum with compile-time reflection
 * support.
 *
 * Example:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  union MyUnion {
 *    1: i32 a
 *    2: string b
 *    3: double c
 *  }
 *
 *  enum MyEnum { a, b, c }
 *
 *  /////////////
 *  // foo.cpp //
 *  /////////////
 *
 *  // yields `std::true_type`
 *  using result1 = is_reflectable_enum<MyEnum>;
 *
 *  // yields `std::false_type`
 *  using result2 = is_reflectable_enum<MyUnion>;
 *
 *  // yields `std::false_type`
 *  using result3 = is_reflectable_enum<void>;
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename T>
using is_reflectable_enum = fatal::has_enum_traits<T>;

//////////////////////////////////
// SECTION: VARIANT (UNION) API //
//////////////////////////////////

/**
 * Holds reflection metadata for a given union.
 *
 * NOTE: this class template is only intended to be instantiated by Thrift.
 * Users should ignore the template parameters taken by it and focus simply on
 * the members provided.
 *
 * For the examples below, consider code generated for this Thrift file:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  union MyUnion {
 *    1: i32 a
 *    2: string b
 *    3: double c
 *  } (
 *    some.annotation = "some value",
 *    another.annotation = "another value",
 *  )
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename T>
struct reflected_variant {
  /**
   * A type alias for the union itself.
   *
   * Example:
   *
   *  using info = reflect_variant<MyUnion>;
   *
   *  // yields `MyUnion`
   *  using result = info::type;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using type = T;

  /**
   * An alias to `fatal::variant_traits`.
   *
   * See `fatal::variant_traits`, from the Fatal library, for more information.
   *
   * Example:
   *
   *  using info = reflect_variant<MyUnion>;
   *  using traits = info::traits;
   *
   *  // yields `MyUnion::Type::a`
   *  auto result = traits::array::ids::data[0];
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using traits = fatal::variant_traits<type>;

  /**
   * The reflection metadata tag for the Thrift file where this union is
   * declared.
   *
   * Example:
   *
   *  using info = reflect_variant<MyUnion>;
   *
   *  // yields `My::Namespace::MyModule_tags::module`
   *  using result = info::module;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using module = typename traits::metadata::module;

  /**
   * An instantiation of `reflected_annotations` representing the annotations
   * declared for this type in the Thrift file.
   *
   * Example:
   *
   *  using info = reflect_variant<MyUnion>;
   *
   *  // yields `fatal::sequence<char,
   *  //   'a', 'n', 'o', 't', 'h', 'e', 'r', ' ', 'v', 'a', 'l', 'u', 'e'
   *  // >`
   *  using result = info::annotations::values::another_annotation;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using annotations = typename traits::metadata::annotations;

  /**
   * A unique identifier generated by thrift for this structure.
   *
   * NOTE: this is a legacy feature and should be avoided on new code.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using legacy_id = typename traits::metadata::legacy_id;

  /**
   * Gets the member descriptor for the field with given `Name`.
   *
   * See `fatal::variant_type_descriptor`, from the Fatal library, for more
   * information.
   *
   * Example:
   *
   *  using id_traits = fatal::enum_traits<MyUnion::Type>;
   *  using info = reflect_variant<MyUnion>;
   *  using member_info = info::by_name<id_traits::str::a>;
   *
   *  MyUnion u;
   *  u.set_a(10);
   *
   *  // yields `10`
   *  auto result = member_info::get(u);
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  template <typename Name>
  using by_name = fatal::get<
      typename traits::descriptors,
      Name,
      apache::thrift::detail::reflection_impl::variant_member_name>;

  /**
   * Gets the member descriptor for the field with given `TypeId`.
   *
   * See `fatal::variant_type_descriptor`, from the Fatal library, for more
   * information.
   *
   * Example:
   *
   *  using id_traits = fatal::enum_traits<MyUnion::Type>;
   *  using info = reflect_variant<MyUnion>;
   *  using member_info = info::by_type_id<MyUnion::Type::a>;
   *
   *  MyUnion u;
   *  u.set_a(10);
   *
   *  // yields `10`
   *  auto result = member_info::get(u);
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  template <typename type::Type TypeId>
  using by_type_id = fatal::get<
      typename traits::descriptors,
      std::integral_constant<typename type::Type, TypeId>,
      fatal::get_type::id>;

  /**
   * Gets the member descriptor for the field with given `FieldId`.
   *
   * See `fatal::variant_type_descriptor`, from the Fatal library, for more
   * information.
   *
   * Example:
   *
   *  using id_traits = fatal::enum_traits<MyUnion::Type>;
   *  using info = reflect_variant<MyUnion>;
   *  using member_info = info::by_field_id<1>;
   *
   *  MyUnion u;
   *  u.set_a(10);
   *
   *  // yields `10`
   *  auto result = member_info::get(u);
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  template <field_id_t FieldId>
  using by_field_id = fatal::get<
      typename traits::descriptors,
      std::integral_constant<field_id_t, FieldId>,
      apache::thrift::detail::reflection_impl::variant_member_field_id>;
};

/**
 * Retrieves reflection metadata (as a `reflected_variant`) associated with the
 * given union.
 *
 * If the given type is not a Thrift union, or if there's no reflection
 * metadata available for it, compilation will fail.
 *
 * See the documentation on `reflected_variant` (above) for more information on
 * the returned type.
 *
 * Example:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  union MyUnion {
 *    1: i32 a
 *    2: string b
 *    3: double c
 *  }
 *
 *  //////////////////
 *  // whatever.cpp //
 *  //////////////////
 *  using info = reflect_variant<My::Namespace::MyUnion>;
 *
 *  // yields `MyUnion`
 *  auto result = info::type;
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename T>
using reflect_variant = reflected_variant<T>;

/**
 * Represents Thrift specific metadata for a given union's member.
 *
 * This is exposed as the metadata for Fatal's `variant_member_descriptor`.
 *
 * For the examples below, consider code generated for this Thrift file:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  union MyUnion {
 *    1: i32 a
 *    2: string b
 *    3: double c
 *  }
 *
 * Example:
 *
 *  //////////////////
 *  // whatever.cpp //
 *  //////////////////
 *  using info = reflect_variant<My::Namespace::MyUnion>;
 *  using metadata = info::by_type<double>::metadata;
 *
 *  // yields "c"
 *  auto result = fatal::z_data<metadata::name>();
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename Traits>
struct reflected_variant_member_metadata {
  /**
   * A compile-time string representing the name of this member.
   *
   * Example:
   *
   *  using info = reflect_variant<My::Namespace::MyUnion>;
   *
   *  // yields "c"
   *  auto result = fatal::z_data<info::by_type<double>::metadata::name>();
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using name = typename Traits::name;

  /**
   * A tag type representing the name of this member.
   */
  using tag = typename Traits::tag;

  /**
   * A `std::integral_constant` of type `field_id_t` representing the Thrift
   * field id for this member.
   *
   * Example:
   *
   *  using info = reflect_variant<My::Namespace::MyUnion>;
   *
   *  // yields `3`
   *  auto result = info::by_type<double>::metadata::id::value;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using id = std::integral_constant<field_id_t, Traits::id>;

  /**
   * The type class for this member.
   *
   * Example:
   *
   *  using info = reflect_variant<My::Namespace::MyUnion>;
   *
   *  // yields `type_class::floating_point`
   *  auto result = info::by_type<double>::metadata::id;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using type_class = typename Traits::type_class;
};

/**
 * Tells whether the given type is a Thrift union with compile-time reflection
 * support.
 *
 * Example:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  union MyUnion {
 *    1: i32 a
 *    2: string b
 *    3: double c
 *  }
 *
 *  enum MyEnum { a, b, c }
 *
 *  /////////////
 *  // foo.cpp //
 *  /////////////
 *
 *  // yields `std::true_type`
 *  using result1 = is_reflectable_union<MyUnion>;
 *
 *  // yields `std::false_type`
 *  using result2 = is_reflectable_union<MyEnum>;
 *
 *  // yields `std::false_type`
 *  using result3 = is_reflectable_union<void>;
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename T>
using is_reflectable_union = fatal::has_variant_traits<T>;
} // namespace apache::thrift

#include <thrift/lib/cpp2/reflection/internal/reflection-inl-post.h>

#endif // THRIFT_FATAL_REFLECTION_H_
