Skip to content

[Feature request] Add exhaustive case matcher #42

@peterfication

Description

@peterfication

@dblock Thanks for making this gem! :)

One use case I have for enums is that I want to use them in case statements. A nice feature in Rust is exhaustive matching. Obviously, this doesn't work in native Ruby because of the missing types. However, in ruby-enum, it can work, because we have the information about which cases should be handled.

A possible drawback of this is that such a check adds additional steps to each call of the case statement and makes the runtime of the code slower.

I implemented a quick version of this in a project of mine and would be happy to contribute it to this project. A default/else block could be added as well.

Let me know what you think about it :)

##
# Adds a method to an enum class that allows for exhaustive matching on a value.
#
# @example
# class Color
#   include Ruby::Enum
#   include Ruby::Enum::Ecase
#
#   define :RED, :red
#   define :GREEN, :green
#   define :BLUE, :blue
# end
#
# Color.ecase(Color::RED, {
#   [Color::RED, Color::GREEN] => -> { puts "red or green" },
#   Color::BLUE => -> { puts "blue" },
# })
module Ruby::Enum::Ecase
  def self.included(klass)
    klass.extend(ClassMethods)
  end

  ##
  # @see Ruby::Enum::Ecase
  module ClassMethods
    class ValuesNotDefinedError < StandardError
    end
    class NotAllCasesHandledError < StandardError
    end

    def ecase(value, cases)
      validate_cases(cases)

      cases.each do |values, block|
        values = [values] unless values.is_a?(Array)
        block.call if values.include?(value)
      end
    end

    private

    def validate_cases(cases)
      all_values = cases.keys.flatten
      superfluous_values = all_values - values
      missing_values = values - all_values

      if superfluous_values.any?
        raise ValuesNotDefinedError, "Value(s) not defined: #{superfluous_values.join(", ")}"
      end
      if missing_values.any? # rubocop:disable Style/GuardClause
        raise NotAllCasesHandledError, "Not all cases handled: #{missing_values.join(", ")}"
      end
    end
  end
end

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions