rubocop-hq/rubocop

View on GitHub
lib/rubocop/cop/style/hash_like_case.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
# frozen_string_literal: true

module RuboCop
  module Cop
    module Style
      # Checks for places where `case-when` represents a simple 1:1
      # mapping and can be replaced with a hash lookup.
      #
      # @example MinBranchesCount: 3 (default)
      #   # bad
      #   case country
      #   when 'europe'
      #     'http://eu.example.com'
      #   when 'america'
      #     'http://us.example.com'
      #   when 'australia'
      #     'http://au.example.com'
      #   end
      #
      #   # good
      #   SITES = {
      #     'europe'    => 'http://eu.example.com',
      #     'america'   => 'http://us.example.com',
      #     'australia' => 'http://au.example.com'
      #   }
      #   SITES[country]
      #
      # @example MinBranchesCount: 4
      #   # good
      #   case country
      #   when 'europe'
      #     'http://eu.example.com'
      #   when 'america'
      #     'http://us.example.com'
      #   when 'australia'
      #     'http://au.example.com'
      #   end
      #
      class HashLikeCase < Base
        include MinBranchesCount

        MSG = 'Consider replacing `case-when` with a hash lookup.'

        # @!method hash_like_case?(node)
        def_node_matcher :hash_like_case?, <<~PATTERN
          (case
            _
            (when
              ${str_type? sym_type?}
              $[!nil? recursive_basic_literal?])+ nil?)
        PATTERN

        def on_case(node)
          return unless min_branches_count?(node)

          hash_like_case?(node) do |condition_nodes, body_nodes|
            if nodes_of_same_type?(condition_nodes) && nodes_of_same_type?(body_nodes)
              add_offense(node)
            end
          end
        end

        private

        def nodes_of_same_type?(nodes)
          nodes.all? { |node| node.type == nodes.first.type }
        end
      end
    end
  end
end