Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature request] Add exhaustive case matcher #42

Closed
peterfication opened this issue Jan 3, 2024 · 1 comment · Fixed by #43
Closed

[Feature request] Add exhaustive case matcher #42

peterfication opened this issue Jan 3, 2024 · 1 comment · Fixed by #43

Comments

@peterfication
Copy link
Collaborator

peterfication commented Jan 3, 2024

@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
@dblock
Copy link
Owner

dblock commented Jan 4, 2024

I'd love this feature in Ruby::Enum! Please contribute.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants