4

I am using Apache commons CLI for command line parsing in a Scala utility application. One of the arguments is a database port number (--port=) that overrides the default "5432" (for PostgreSQL). I am trying to use the Option class to assist in the validation. Here is the code I came up with. Is there a better way to do the validation?

val port = Option(commandLine.getOptionValue("port", "5432")) map {
  try {
    val value = Integer.parseInt(_)
    if (value < 1 || value > 65535) throw new NumberFormatException
    value
  } catch {
    case ex: NumberFormatException =>
      throw new
        IllegalArgumentException("the port argument must be a number between 1 and 65535")
  }
} get

The port number must be an integer between 1 and 65535, inclusive.

Would it be better to do this? Why or why not?

val port = Option(commandLine.getOptionValue("port")) map {
  try {
    val value = Integer.parseInt(_)
    if (value < 1 || value > 65535) throw new NumberFormatException
    value
  } catch {
    case ex: NumberFormatException =>
      throw new
        IllegalArgumentException("the port argument must be a number between 1 and 65535")
  }
} getOrElse(5432)
2
  • 1
    What I'm missing in the std lib is a method String => Option[Int]. Commented Sep 22, 2011 at 12:32
  • 2
    @ziggystar: If you can use Scalaz, it has a method parsent: String => Validation[NumberFormatException, Int]. Calling toOption on the validation will give you Option[Int]. Commented Sep 22, 2011 at 12:40

2 Answers 2

7

I admit I'm not 100% sure, what you want to be thrown in case something goes wrong, or if the 5432 is a default port for every wrong value, but here is what I would do:

def getPort(candidate: Option[String]) = candidate
   .map { _.toInt } // throws NumberFormatException
   .filter { v => v > 0 && v <= 65535 } // returns Option[Int]
   .getOrElse { throw new IllegalArgumentException("error message") } // return an Int or throws an exception
Sign up to request clarification or add additional context in comments.

3 Comments

I initially had something similar (with the map(_.toInt)), but I did not want to have to duplicate the error message.
I think the code should throw an exception if the port is illegal. It should only use 5432 if no port was provided.
Then you can define the given method with a default value: def getPort(candidate: Option[String] = Some("5432")) = ...
2

I guess it's a good time for me to explore Validation.

import scalaz._
import Scalaz._

val port = {
  def checkRange(port: Int): Validation[String, Int] = {
    if (port < 1 || port > 65535) "port not in [1-65535]".fail
    else port.success
  }
  commandLine.getOptionValue("port", "5432")
    .parseInt.flatMap(checkRange) match {
    case Failure(e) => throw new IllegalArgumentException(e.toString)
    case Success(port) => port
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.