Handling all the enums

I have a set of values, and I want to make sure each case is handled. I am curious what the Crystal way is to do this. At first I tried enums, but I don’t think there’s a compiler mechanism forcing me to address each enum value. So then I tried separate classes for each value, and method overloading appears to do the trick. I want to share these to get some feedback from more experienced folks.

Is the class + method overload approach the “crystal way”?

note: the OptionParser functionality isn’t important here, it’s just a mechanism to let me select the value at runtime

enum approach

While the enum definition looks very nice, I don’t think this quite does what I want. I can delete any of the case conditions, and it will happily compile. If I remove the case statement, there’s just no output, which is expected. Is there a way for the compiler to check whether I’ve covered all cases? I don’t think there’s a way for methods to pattern match on values, is there? Methods only pattern match on types I think.

# using enums appears to let some values be missed
# (i.e. no else statement is required)

require "option_parser"

enum Command
  Get
  Put
  Delete
  Unknown
end

command = Command::Unknown

OptionParser.parse do |parser|
  parser.on("get", "get") { command = Command::Get }
  parser.on("put", "put") { command = Command::Put }
  parser.on("del", "delete") { command = Command::Delete }
end

case command
when Command::Get
  puts "get"
when Command::Put
  puts "put"
when Command::Delete
  puts "delete"
else
  STDERR.puts "E: unknown"
  exit 1
end

class + method overload approach

This seems to do what I want. My only question is if this is the cleanest way to get this behavior. If I comment out any of the run(...) definitions, it fails to compile - so I can be confident that I’m covering each case.

 30 | run(command)
          ^------
Error: expected argument #1 to 'run' to be Delete, Get or Unknown, not (Delete | Get | Put | Unknown)

Overloads are:
 - run(c : Get)
 - run(c : Delete)
 - run(c : Unknown)
*** Error code 1

code:

# using classes appears to require that all types are handled

require "option_parser"

class Get; end
class Put; end
class Delete; end
class Unknown; end

command = Unknown.new

OptionParser.parse do |parser|
  parser.on("get", "get") { command = Get.new }
  parser.on("put", "put") { command = Put.new }
  parser.on("del", "delete") { command = Delete.new }
end

def run(c : Get)
  puts "get"
end

def run(c : Put)
  puts "put"
end

def run(c : Delete)
  puts "delete"
end

def run(c : Unknown)
  STDERR.puts "E: unknown"
end

run(command)

Yes. Checkout case - Crystal.

1 Like

Excellent, thank you!!