module SyntaxTree::HashKeyFormatter

def for(container)

def for(container)
  (assocs = container.assocs).each_with_index do |assoc, index|
    if assoc.is_a?(AssocSplat)
      # Splat nodes do not impact the formatting choice.
    elsif assoc.value.nil?
      # If the value is nil, then it has been omitted. In this case we
      # have to match the existing formatting because standardizing would
      # potentially break the code. For example:
      #
      #     { first:, "second" => "value" }
      #
      return Identity.new
    else
      # Otherwise, we need to check the type of the key. If it's a label
      # or dynamic symbol, we can use labels. If it's a symbol literal
      # then it needs to match a certain pattern to be used as a label. If
      # it's anything else, then we need to use hash rockets.
      case assoc.key
      when Label, DynaSymbol
        # Here labels can be used.
      when SymbolLiteral
        # When attempting to convert a hash rocket into a hash label,
        # you need to take care because only certain patterns are
        # allowed. Ruby source says that they have to match keyword
        # arguments to methods, but don't specify what that is. After
        # some experimentation, it looks like it's:
        value = assoc.key.value.value
        if !value.match?(/^[_A-Za-z]/) || value.end_with?("=")
          if omitted_value?(assocs[(index + 1)..])
            return Identity.new
          else
            return Rockets.new
          end
        end
      else
        if omitted_value?(assocs[(index + 1)..])
          return Identity.new
        else
          return Rockets.new
        end
      end
    end
  end
  Labels.new
end

def omitted_value?(assocs)

def omitted_value?(assocs)
  assocs.any? { |assoc| !assoc.is_a?(AssocSplat) && assoc.value.nil? }
end