text: def _go_template_impl(ctx):
      input = ctx.files.srcs
      output = ctx.outputs.out
  
      args = ["-o=%s" % output.path] + [f.path for f in input]
  
      ctx.actions.run(
          inputs = input,
          outputs = [output],
          mnemonic = "GoGenericsTemplate",
          progress_message = "Building Go template %s" % ctx.label,
          arguments = args,
          executable = ctx.executable._tool,
      )
  
      return struct(
          types = ctx.attr.types,
          opt_types = ctx.attr.opt_types,
          consts = ctx.attr.consts,
          opt_consts = ctx.attr.opt_consts,
          deps = ctx.attr.deps,
          file = output,
      )
  
  """
  Generates a Go template from a set of Go files.
  
  A Go template is similar to a go library, except that it has certain types that
  can be replaced before usage. For example, one could define a templatized List
  struct, whose elements are of type T, then instantiate that template for
  T=segment, where "segment" is the concrete type.
  
  Args:
    name: the name of the template.
    srcs: the list of source files that comprise the template.
    types: the list of generic types in the template that are required to be specified.
    opt_types: the list of generic types in the template that can but aren't required to be specified.
    consts: the list of constants in the template that are required to be specified.
    opt_consts: the list of constants in the template that can but aren't required to be specified.
    deps: the list of dependencies.
  """
  go_template = rule(
      implementation = _go_template_impl,
      attrs = {
          "srcs": attr.label_list(mandatory = True, allow_files = True),
          "deps": attr.label_list(allow_files = True),
          "types": attr.string_list(),
          "opt_types": attr.string_list(),
          "consts": attr.string_list(),
          "opt_consts": attr.string_list(),
          "_tool": attr.label(executable = True, cfg = "host", default = Label("//tools/go_generics/go_merge")),
      },
      outputs = {
          "out": "%{name}_template.go",
      },
  )
  
  def _go_template_instance_impl(ctx):
      template = ctx.attr.template
      output = ctx.outputs.out
  
      # Check that all required types are defined.
      for t in template.types:
          if t not in ctx.attr.types:
              fail("Missing value for type %s in %s" % (t, ctx.attr.template.label))
  
      # Check that all defined types are expected by the template.
      for t in ctx.attr.types:
          if (t not in template.types) and (t not in template.opt_types):
              fail("Type %s it not a parameter to %s" % (t, ctx.attr.template.label))
  
      # Check that all required consts are defined.
      for t in template.consts:
          if t not in ctx.attr.consts:
              fail("Missing value for constant %s in %s" % (t, ctx.attr.template.label))
  
      # Check that all defined consts are expected by the template.
      for t in ctx.attr.consts:
          if (t not in template.consts) and (t not in template.opt_consts):
              fail("Const %s it not a parameter to %s" % (t, ctx.attr.template.label))
  
      # Build the argument list.
      args = ["-i=%s" % template.file.path, "-o=%s" % output.path]
      args += ["-p=%s" % ctx.attr.package]
  
      if len(ctx.attr.prefix) > 0:
          args += ["-prefix=%s" % ctx.attr.prefix]
  
      if len(ctx.attr.suffix) > 0:
          args += ["-suffix=%s" % ctx.attr.suffix]
  
      args += [("-t=%s=%s" % (p[0], p[1])) for p in ctx.attr.types.items()]
      args += [("-c=%s=%s" % (p[0], p[1])) for p in ctx.attr.consts.items()]
      args += [("-import=%s=%s" % (p[0], p[1])) for p in ctx.attr.imports.items()]
  
      ctx.actions.run(
          inputs = [template.file],
          outputs = [output],
          mnemonic = "GoGenericsInstance",
          progress_message = "Building Go template instance %s" % ctx.label,
          arguments = args,
          executable = ctx.executable._tool,
      )
  
      # TODO: How can we get the dependencies out?
      return struct(
          files = depset([output]),
      )
  
  """
  Instantiates a Go template by replacing all generic types with concrete ones.
  
  Args:
    name: the name of the template instance.
    template: the label of the template to be instatiated.
    prefix: a prefix to be added to globals in the template.
    suffix: a suffix to be added to global in the template.
    types: the map from generic type names to concrete ones.
    consts: the map from constant names to their values.
    imports: the map from imports used in types/consts to their import paths.
    package: the name of the package the instantiated template will be compiled into.
  """
  go_template_instance = rule(
      implementation = _go_template_instance_impl,
      attrs = {
          "template": attr.label(mandatory = True, providers = ["types"]),
          "prefix": attr.string(),
          "suffix": attr.string(),
          "types": attr.string_dict(),
          "consts": attr.string_dict(),
          "imports": attr.string_dict(),
          "package": attr.string(mandatory = True),
          "out": attr.output(mandatory = True),
          "_tool": attr.label(executable = True, cfg = "host", default = Label("//tools/go_generics")),
      },
  )
  