Created
June 27, 2017 08:32
-
-
Save friedbrice/33302de9af107b809c19da2bac83baab to your computer and use it in GitHub Desktop.
Zero-dependency Scala 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 CommandLineDemo extends App { | |
| val clargs: CLArgs = CLArgs(args) | |
| val all: Option[Unit] = clargs.flag('a', "all") | |
| val list: Option[Unit] = clargs.shortFlag('l') | |
| val ctl: Option[Unit] = clargs.longFlag("show-control-chars") | |
| val tab: Option[String] = clargs.value('T', "tabsize") | |
| println(s"$all-$list-$ctl-$tab") | |
| } | |
| class CLArgs(rawArgs: Array[String]) { | |
| import CLArgs._ | |
| private val args: Args = parse(rawArgs) | |
| def flag(short: Char, long: String): Option[Unit] = | |
| shortFlag(short) \/ longFlag(long) | |
| def shortFlag(short: Char): Option[Unit] = | |
| checkIf(args.shortFlags.contains(short)) | |
| def longFlag(long: String): Option[Unit] = | |
| checkIf(args.longFlags.contains(long)) | |
| def value(short: Char, long: String): Option[String] = | |
| shortValue(short) \/ longValue(long) | |
| def shortValue(short: Char): Option[String] = | |
| args.shortValues.get(short) | |
| def longValue(long: String): Option[String] = | |
| args.longValues.get(long) | |
| } | |
| object CLArgs { | |
| def apply(rawArgs: Array[String]): CLArgs = new CLArgs(rawArgs) | |
| private sealed trait Token extends Product with Serializable | |
| private case class Short(c: Char) extends Token | |
| private case class Long(s: String) extends Token | |
| private case class Value(v: String) extends Token | |
| private case class Args( | |
| shortFlags: List[Char], | |
| longFlags: List[String], | |
| shortValues: Map[Char, String], | |
| longValues: Map[String, String] | |
| ) | |
| private val emptyArgs = Args(List.empty, List.empty, Map.empty, Map.empty) | |
| private def tokenize(args: List[String]): List[Token] = | |
| args.flatMap(arg => arg.split("=")).flatMap({ | |
| case s if s.startsWith("--") => List(Long(s.substring(2))) | |
| case s if s.startsWith("-") => s.substring(1).toList.map(Short) | |
| case s => List(Value(s)) | |
| }) | |
| private def analyze(tokens: List[Token]): Args = { | |
| val pairs = if (tokens.isEmpty) Nil else tokens.zip(tokens.tail) | |
| pairs.foldLeft(emptyArgs) { (args, pair) => | |
| (pair._1, pair._2) match { | |
| case (Short(c), Value(v)) => | |
| args.copy(shortValues = args.shortValues.updated(c, v)) | |
| case (Short(c), _) => | |
| args.copy(shortFlags = c +: args.shortFlags) | |
| case (Long(s), Value(v)) => | |
| args.copy(longValues = args.longValues.updated(s, v)) | |
| case (Long(s), _) => | |
| args.copy(longFlags = s +: args.longFlags) | |
| case _ => args | |
| } | |
| } | |
| } | |
| private def parse(args: Array[String]): Args = analyze(tokenize(args.toList)) | |
| private implicit class OptionOps[A](self: Option[A]) { | |
| def \/[A2 >: A](other: Option[A2]): Option[A2] = self.fold(other)(_ => self) | |
| } | |
| private def checkIf(p: Boolean): Option[Unit] = if (p) Some(()) else None | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment