Last active
December 24, 2015 01:59
-
-
Save bruth/6727690 to your computer and use it in GitHub Desktop.
Simple command line parser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
object cli { | |
case class ArgumentsRequired(args: List[Any]) extends Exception { | |
override def getMessage: String = { | |
"required arguments: %s".format(this.args.mkString(", ")) | |
} | |
} | |
case class UnknownArguments(args: List[Any]) extends Exception { | |
override def getMessage: String = { | |
"unknown arguments: %s".format(this.args.mkString(", ")) | |
} | |
} | |
/** | |
* parseOptions parses command line arguments based on the provided required | |
* and optional arguments. If not all the required arguments have been | |
* fulfilled or the unknown arguments are provided an exception will be | |
* thrown. | |
* | |
* @param args A list of strings that contain the options being parsed. | |
* @param required A list of symbols that will be mapped to positional | |
* arguments (strings that are not flag-based). | |
* @param optional A map of string -> symbol representing the argument flags | |
* and corresponding symbol to be used in the options map. | |
* @param options A map of symbol -> string which contains the parsed (or | |
* predefined) options. | |
* @return A map of symbol -> string containing the parsed options. | |
*/ | |
def parseOptions(args: List[String], required: List[Symbol], optional: Map[String, Symbol], options: Map[Symbol, String]): Map[Symbol, String] = { | |
args match { | |
// Empty list. Ensure the required arguments have been satisfied. | |
case Nil => { | |
if (required != Nil) { | |
throw new ArgumentsRequired(required) | |
} | |
options | |
} | |
// Keyword arguments | |
case key :: value :: tail if optional.get(key) != None => | |
parseOptions(tail, required, optional, options ++ Map(optional(key) -> value)) | |
// Positional arguments | |
case value :: tail if required != Nil => | |
parseOptions(tail, required.tail, optional, options ++ Map(required.head -> value)) | |
// Unknown argument is received | |
case _ => | |
throw new UnknownArguments(args) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The way I would write this is
The
@tailrec
tag ensures that your recursive method is tail recursive which means you won't blow the stack. Probably not important here, but in general its a good idea to use whenever doing recursion. I, like Mike suggested, would also use theOption
constructs with default args. That is idiomatic Scala and the default arg makes it easier for the user of the method since they don't have to supply those arguments.FWIW, I like your use of recursion in this case, it's probably the most elegant solution. Additionally, your use of pattern guards is cool (I often forget they exist) though I did change the condition checks to use builtins and I didn't know you could do
case Some(value :: tail)
matches - just when I think I know Scala I'm reminded that I don't.