"""Go rules."""

load("@bazel_gazelle//:def.bzl", _gazelle = "gazelle")
load("@io_bazel_rules_go//go:def.bzl", "GoLibrary", _go_binary = "go_binary", _go_context = "go_context", _go_embed_data = "go_embed_data", _go_library = "go_library", _go_path = "go_path", _go_test = "go_test")
load("@io_bazel_rules_go//proto:def.bzl", _go_grpc_library = "go_grpc_library", _go_proto_library = "go_proto_library")
load("//tools/bazeldefs:defs.bzl", "select_arch", "select_system")

gazelle = _gazelle
go_embed_data = _go_embed_data
go_path = _go_path

def _go_proto_or_grpc_library(go_library_func, name, **kwargs):
    deps = [
        dep.replace("_proto", "_go_proto")
        for dep in (kwargs.pop("deps", []) or [])
    ]
    go_library_func(
        name = name + "_go_proto",
        importpath = "gvisor.dev/gvisor/" + native.package_name() + "/" + name + "_go_proto",
        proto = ":" + name + "_proto",
        deps = deps,
        **kwargs
    )

def go_proto_library(name, **kwargs):
    _go_proto_or_grpc_library(_go_proto_library, name, **kwargs)

def go_grpc_and_proto_libraries(name, **kwargs):
    _go_proto_or_grpc_library(_go_grpc_library, name, **kwargs)

def go_binary(name, static = False, pure = False, x_defs = None, system_malloc = False, **kwargs):
    """Build a go binary.

    Args:
        name: name of the target.
        static: build a static binary.
        pure: build without cgo.
        x_defs: additional definitions.
        **kwargs: rest of the arguments are passed to _go_binary.
    """
    if static:
        kwargs["static"] = "on"
    if pure:
        kwargs["pure"] = "on"
    _go_binary(
        name = name,
        x_defs = x_defs,
        **kwargs
    )

def go_importpath(target):
    """Returns the importpath for the target."""
    return target[GoLibrary].importpath

def go_library(name, arch_deps = [], **kwargs):
    _go_library(
        name = name,
        importpath = "gvisor.dev/gvisor/" + native.package_name(),
        **kwargs
    )

def go_test(name, pure = False, library = None, **kwargs):
    """Build a go test.

    Args:
        name: name of the output binary.
        pure: should it be built without cgo.
        library: the library to embed.
        **kwargs: rest of the arguments to pass to _go_test.
    """
    if pure:
        kwargs["pure"] = "on"
    if library:
        kwargs["embed"] = [library]
    _go_test(
        name = name,
        **kwargs
    )

def go_rule(rule, implementation, **kwargs):
    """Wraps a rule definition with Go attributes.

    Args:
      rule: rule function (typically rule or aspect).
      implementation: implementation function.
      **kwargs: other arguments to pass to rule.

    Returns:
        The result of invoking the rule.
    """
    attrs = kwargs.pop("attrs", dict())
    attrs["_go_context_data"] = attr.label(default = "@io_bazel_rules_go//:go_context_data")
    attrs["_stdlib"] = attr.label(default = "@io_bazel_rules_go//:stdlib")
    toolchains = kwargs.get("toolchains", []) + ["@io_bazel_rules_go//go:toolchain"]
    return rule(implementation, attrs = attrs, toolchains = toolchains, **kwargs)

def go_embed_libraries(target):
    if hasattr(target.attr, "embed"):
        return target.attr.embed
    return []

def go_context(ctx, goos = None, goarch = None, std = False):
    """Extracts a standard Go context struct.

    Args:
      ctx: the starlark context (required).
      goos: the GOOS value.
      goarch: the GOARCH value.
      std: ignored.

    Returns:
      A context Go struct with pointers to Go toolchain components.
    """

    # We don't change anything for the standard library analysis. All Go files
    # are available in all instances. Note that this includes the standard
    # library sources, which are analyzed by nogo.
    go_ctx = _go_context(ctx)
    if goos == None:
        goos = go_ctx.sdk.goos
    elif goos != go_ctx.sdk.goos:
        fail("Internal GOOS (%s) doesn't match GoSdk GOOS (%s)." % (goos, go_ctx.sdk.goos))
    if goarch == None:
        goarch = go_ctx.sdk.goarch
    elif goarch != go_ctx.sdk.goarch:
        fail("Internal GOARCH (%s) doesn't match GoSdk GOARCH (%s)." % (goarch, go_ctx.sdk.goarch))
    return struct(
        go = go_ctx.go,
        env = go_ctx.env,
        nogo_args = [],
        stdlib_srcs = go_ctx.sdk.srcs,
        runfiles = depset([go_ctx.go] + go_ctx.sdk.srcs + go_ctx.sdk.tools + go_ctx.stdlib.libs),
        goos = go_ctx.sdk.goos,
        goarch = go_ctx.sdk.goarch,
        tags = go_ctx.tags,
    )

def select_goarch():
    return select_arch(arm64 = "arm64", amd64 = "amd64")

def select_goos():
    return select_system(linux = "linux")