load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("//common:crubit_wrapper_macros_oss.bzl", "crubit_rust_test")
load(
    "//rs_bindings_from_cc/bazel_support:additional_rust_srcs_for_crubit_bindings_aspect_hint.bzl",
    "additional_rust_srcs_for_crubit_bindings",
)
load(
    "//rs_bindings_from_cc/bazel_support:rust_bindings_from_cc_cli_flag_aspect_hint.bzl",
    "rust_bindings_from_cc_cli_flag",
)
load(
    "//rs_bindings_from_cc/test/golden:golden_test.bzl",
    "golden_test",
)

package(default_applicable_licenses = ["//:license"])

exports_files([
    "types_in_extra_cpp_srcs_extra.cc",
    "types_in_extra_cpp_srcs_extra.rs",
])

TESTS = [name[:-2] for name in glob(
    ["*.h"],
    exclude = [
        "*namespaces_json.h",
        "not_crubit_exposed.h",
        "template_def_with_crubit.h",
        "template_fwd_without_crubit.h",
    ],
)]

# Maps a test to the list of tests it depends on.
#
# Valid keys:
#     basenames of tests (e.g. "types" is a valid key, because we
#     have rs_bindings_from_cc/test/golden/types.h.
# Valid values: a list of:
#     basenames of tests (e.g. "types")
#     other Bazel targets (e.g. "//foo/bar:baz")
DEPS = {
    "user_of_unsupported": ["unsupported"],
    "user_of_imported_type": ["trivial_type"],
    "user_of_base_class": ["inheritance"],
    "includes": [
        "comment",
        "namespace",
        "types",
        "unions",
        "unsupported",
    ],
    "types": ["//support/rs_std:char"],
    "composable_bridging": [
        "//support/rs_std:slice_ref",
        "@abseil-cpp//absl/strings:string_view",
    ],
    "definition_of_forward_declaration": [
        "forward_declaration",
    ],
    "uses_not_crubit_exposed": ["not_crubit_exposed"],
    "depends_on_nested_types": ["nested_types"],
    "template_inst": [
        "template_def_with_crubit",
        "template_fwd_without_crubit",
    ],
    "callables": ["//support/rs_std:dyn_callable"],
    "bridge_wrapping_alias": ["//support:bridge_cpp"],
}

TAGS = {}

ASPECT_HINTS = {
    "composable_bridging_template_type": [
        "//features:wrapper",
    ],
    "template_inst": [
        "//features:wrapper",
    ],
    "deprecated_experimental": [
        "//features:experimental",
    ],
    "nodiscard_experimental": [
        "//features:experimental",
    ],
    "blocklist_template_paths": [
        "//features:template_instantiation",
        ":template_blocklist_path_regex",
    ],
    "template_instantiation": [
        "//features:template_instantiation",
    ],
    "conversion_operators": [
        "//features:assume_this_lifetimes",
    ],
    "typedef_incomplete_types": [
        "//features:wrapper",
    ],
    "dunder_types": [
        "//features:wrapper",
    ],
    "types_in_extra_cpp_srcs": [
        ":types_in_extra_cpp_srcs_aspect_hint",
    ],
    "nonstandard_calling_convention": [
        "//features:experimental",
    ],
    "bridge_wrapping_alias": [
        "//features:template_instantiation",
        ":bridge_wrapping_alias_aspect_hint",
    ],
}

additional_rust_srcs_for_crubit_bindings(
    name = "bridge_wrapping_alias_aspect_hint",
    srcs = ["bridge_wrapping_alias_extra.rs"],
)

additional_rust_srcs_for_crubit_bindings(
    name = "types_in_extra_cpp_srcs_aspect_hint",
    srcs = ["types_in_extra_cpp_srcs_extra.rs"],
    cpp_srcs = ["types_in_extra_cpp_srcs_extra.cc"],
)

rust_bindings_from_cc_cli_flag(
    name = "enable_kythe_annotations",
    flags = "--kythe_annotations=True",
)

rust_bindings_from_cc_cli_flag(
    name = "template_blocklist_path_regex",
    # Be careful: everything after the = is passed verbatim, including quotes
    flags = "--template_blocklist_path_regex=.*blocklist_template_paths.*",
)

[cc_library(
    name = name + "_cc",
    hdrs = [name + ".h"],
    aspect_hints = [
        "//features:supported",
    ] + (ASPECT_HINTS[name] if name in ASPECT_HINTS else []) + ([":enable_kythe_annotations"] if name.endswith("_annotations") else []),
    deps = [
        ((d + "_cc") if d in TESTS else d)
        for d in (DEPS[name] if name in DEPS else [])
    ],
) for name in TESTS]

[golden_test(
    name = name + "_test",
    basename = name,
    cc_library = name + "_cc",
    golden_cc = name + "_rs_api_impl.cc",
    golden_rs = name + "_rs_api.rs",
    tags = [tag for tag in (TAGS[name] if name in TAGS else [])],
) for name in TESTS]

NON_BUILDABLE_TEST = [
    "callables",

    # The rust type is not defined in the C++ header so it cannot be built.
    "crubit_internal_rust_type",

    # We can't support additional Rust srcs so we can't run the test.
    "composable_bridging",
    "composable_bridging_template_type",

    # Rust type is not defined in the C++ header, but in an extra .cc file.
    "types_in_extra_cpp_srcs",
]

BUILDABLE_TESTS = [name for name in TESTS if name not in NON_BUILDABLE_TEST]

[crubit_rust_test(
    name = name + "_rs_test",
    srcs = ["empty_rs_test.rs"],
    cc_deps = ["%s_cc" % name],
) for name in BUILDABLE_TESTS]

cc_library(
    name = "namespaces_json",
    hdrs = ["namespaces_json.h"],
    aspect_hints = [
        "//features:supported",
    ],
)

golden_test(
    name = "namespaces_json_test",
    basename = "namespaces_json",
    cc_library = "namespaces_json",
    golden_namespaces = "namespaces_json.json",
)

bzl_library(
    name = "golden_test",
    srcs = ["golden_test.bzl"],
    visibility = ["//visibility:private"],
    deps = [
        "//rs_bindings_from_cc/bazel_support:rust_bindings_from_cc_aspect",
        "//rs_bindings_from_cc/bazel_support:rust_bindings_from_cc_utils_bzl",
        "@rules_cc//cc/common",
    ],
)

cc_library(
    name = "not_crubit_exposed",
    hdrs = ["not_crubit_exposed.h"],
)

# A cc_library that defines MyTemplate<T>
cc_library(
    name = "template_def_with_crubit",
    hdrs = ["template_def_with_crubit.h"],
    aspect_hints = [
        "//features:wrapper",
    ],
)

# A cc_library that forward declares MyTemplate<T>, independently of template_def_with_crubit
cc_library(
    name = "template_fwd_without_crubit",
    hdrs = ["template_fwd_without_crubit.h"],
)
