pax_global_header00006660000000000000000000000064127271521000014507gustar00rootroot0000000000000052 comment=97625401db82ad6d2ecfb52903e312d06246a7fe scopt-3.5.0/000077500000000000000000000000001272715210000126445ustar00rootroot00000000000000scopt-3.5.0/.gitignore000066400000000000000000000000711272715210000146320ustar00rootroot00000000000000src_managed target project/boot lib_managed *.i?? .idea/ scopt-3.5.0/.travis.yml000066400000000000000000000006761272715210000147660ustar00rootroot00000000000000sudo: false # These directories are cached to S3 at the end of the build cache: directories: - $HOME/.ivy2/cache - $HOME/.sbt/boot/ language: scala script: - sbt ++$TRAVIS_SCALA_VERSION -Dfile.encoding=UTF8 -J-XX:ReservedCodeCacheSize=256M test # Tricks to avoid unnecessary cache updates - find $HOME/.sbt -name "*.lock" | xargs rm - find $HOME/.ivy2 -name "ivydata-*.properties" | xargs rm scala: - 2.10.6 - 2.11.7 scopt-3.5.0/README.md000066400000000000000000000317771272715210000141420ustar00rootroot00000000000000 [1]: http://scopt.github.io/scopt/3.5.0/api/index.html#scopt.OptionParser scopt ===== scopt is a little command line options parsing library. Sonatype -------- ```scala libraryDependencies += "com.github.scopt" %% "scopt" % "3.5.0" resolvers += Resolver.sonatypeRepo("public") ``` Usage ----- scopt provides two styles of parsing: immutable and mutable. Either case, first you need a case class that represents the configuration: ```scala import java.io.File case class Config(foo: Int = -1, out: File = new File("."), xyz: Boolean = false, libName: String = "", maxCount: Int = -1, verbose: Boolean = false, debug: Boolean = false, mode: String = "", files: Seq[File] = Seq(), keepalive: Boolean = false, jars: Seq[File] = Seq(), kwargs: Map[String,String] = Map()) ``` In immutable parsing style, a config object is passed around as an argument into `action` callbacks. On the other hand, in mutable parsing style you are expected to modify the config object in place. ### Immutable parsing Here's how you create a `scopt.OptionParser[Config]`. See [Scaladoc API][1] for the details on various builder methods. ```scala val parser = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") opt[Int]('f', "foo").action( (x, c) => c.copy(foo = x) ).text("foo is an integer property") opt[File]('o', "out").required().valueName(""). action( (x, c) => c.copy(out = x) ). text("out is a required file property") opt[(String, Int)]("max").action({ case ((k, v), c) => c.copy(libName = k, maxCount = v) }). validate( x => if (x._2 > 0) success else failure("Value must be >0") ). keyValueName("", ""). text("maximum count for ") opt[Seq[File]]('j', "jars").valueName(",...").action( (x,c) => c.copy(jars = x) ).text("jars to include") opt[Map[String,String]]("kwargs").valueName("k1=v1,k2=v2...").action( (x, c) => c.copy(kwargs = x) ).text("other arguments") opt[Unit]("verbose").action( (_, c) => c.copy(verbose = true) ).text("verbose is a flag") opt[Unit]("debug").hidden().action( (_, c) => c.copy(debug = true) ).text("this option is hidden in the usage text") help("help").text("prints this usage text") arg[File]("...").unbounded().optional().action( (x, c) => c.copy(files = c.files :+ x) ).text("optional unbounded args") note("some notes.".newline) cmd("update").action( (_, c) => c.copy(mode = "update") ). text("update is a command."). children( opt[Unit]("not-keepalive").abbr("nk").action( (_, c) => c.copy(keepalive = false) ).text("disable keepalive"), opt[Boolean]("xyz").action( (x, c) => c.copy(xyz = x) ).text("xyz is a boolean property"), opt[Unit]("debug-update").hidden().action( (_, c) => c.copy(debug = true) ).text("this option is hidden in the usage text"), checkConfig( c => if (c.keepalive && c.xyz) failure("xyz cannot keep alive") else success ) ) } // parser.parse returns Option[C] parser.parse(args, Config()) match { case Some(config) => // do stuff case None => // arguments are bad, error message will have been displayed } ``` The above generates the following usage text: ``` scopt 3.x Usage: scopt [update] [options] [...] -f, --foo foo is an integer property -o, --out out is a required file property --max:= maximum count for -j, --jars ,... jars to include --kwargs k1=v1,k2=v2... other arguments --verbose verbose is a flag --help prints this usage text ... optional unbounded args some notes. Command: update [options] update is a command. -nk, --not-keepalive disable keepalive --xyz xyz is a boolean property ``` #### Options Command line options are defined using `opt[A]('f', "foo")` or `opt[A]("foo")` where `A` is any type that is an instance of `Read` typeclass. - `Unit` works as a plain flag `--foo` or `-f` - `Int`, `Long`, `Double`, `String`, `BigInt`, `BigDecimal`, `java.io.File`, `java.net.URI`, and `java.net.InetAddress` accept a value like `--foo 80` or `--foo:80` - `Boolean` accepts a value like `--foo true` or `--foo:1` - `java.util.Calendar` accepts a value like `--foo 2000-12-01` - `scala.concurrent.duration.Duration` accepts a value like `--foo 30s` - A pair of types like `(String, Int)` accept a key-value like `--foo:k=1` or `-f k=1` - A `Seq[File]` accepts a string containing comma-separated values such as `--jars foo.jar,bar.jar` - A `Map[String, String]` accepts a string containing comma-separated pairs like `--kwargs key1=val1,key2=val2` This could be extended by defining `Read` instances in the scope. For example, ```scala object WeekDays extends Enumeration { type WeekDays = Value val Mon, Tue, Wed, Thur, Fri, Sat, Sun = Value } implicit val weekDaysRead: scopt.Read[WeekDays.Value] = scopt.Read.reads(WeekDays withName _) ``` By default these options are optional. #### Short options For plain flags (`opt[Unit]`) short options can be grouped as `-fb` to mean `--foo --bar`. `opt` accepts only a single character, but using `abbr("ab")` a string can be used too: ```scala opt[Unit]("no-keepalive").abbr("nk").action( (x, c) => c.copy(keepalive = false) ) ``` #### Help, Version, and Notes There are special options with predefined action called `help("help")` and `version("version")`, which prints usage text and header text respectively. When `help("help")` is defined, parser will print out short error message when it fails instead of printing the entire usage text. This behavior could be changed by overriding `showUsageOnError` as follows: ```scala override def showUsageOnError = true ``` `note("...")` is used add given string to the usage text. #### Arguments Command line arguments are defined using `arg[A]("")`. It works similar to options, but instead it accepts values without `--` or `-`. By default, arguments accept a single value and are required. ```scala arg[String]("...") ``` #### Occurrence Each opt/arg carries occurrence information `minOccurs` and `maxOccurs`. `minOccurs` specify at least how many times an opt/arg must appear, and `maxOccurs` specify at most how many times an opt/arg may appear. Occurrence can be set using the methods on the opt/arg: ```scala opt[String]('o', "out").required() opt[String]('o', "out").minOccurs(1) // same as above arg[String]("").optional() arg[String]("").minOccurs(0) // same as above arg[String]("...").optional().unbounded() arg[String]("...").minOccurs(0).maxOccurs(1024) // same as above ``` #### Visibility Each opt/arg can be hidden from the usage text using `hidden()` method: ```scala opt[Unit]("debug").hidden().action( (_, c) => c.copy(debug = true) ).text("this option is hidden in the usage text") ``` #### Validation Each opt/arg can carry multiple validation functions. ```scala opt[Int]('f', "foo").action( (x, c) => c.copy(intValue = x) ). validate( x => if (x > 0) success else failure("Option --foo must be >0") ). validate( x => failure("Just because") ) ``` The first function validates if the values are positive, and the second function always fails. #### Check configuration Consistency among the option values can be checked using `checkConfig`. ```scala checkConfig( c => if (c.keepalive && c.xyz) failure("xyz cannot keep alive") else success ) ``` These are called at the end of parsing. #### Commands Commands may be defined using `cmd("update")`. Commands could be used to express `git branch` kind of argument, whose name means something. Using `children` method, a command may define child opts/args that get inserted in the presence of the command. To distinguish commands from arguments, they must appear in the first position within the level. It is generally recommended to avoid mixing args both in parent level and commands to avoid confusion. ```scala cmd("update"). action( (_, c) => c.copy(mode = "update") ). text("update is a command."). children( opt[Unit]("not-keepalive").abbr("nk").action( (_, c) => c.copy(keepalive = false) ).text("disable keepalive"), opt[Boolean]("xyz").action( (x, c) => c.copy(xyz = x) ).text("xyz is a boolean property"), checkConfig( c => if (c.keepalive && c.xyz) failure("xyz cannot keep alive") else success ) ) ``` In the above, `update test.txt` would trigger the update command, but `test.txt update` won't. Commands could be nested into another command as follows: ```scala cmd("backend").text("commands to manipulate backends:\n"). action( (x, c) => c.copy(flag = true) ). children( cmd("update").children( arg[String]("").action( (x, c) => c.copy(a = x) ) ) ) ``` ### Termination Handling By default, when the `--help` or `--version` are invoked, they call `sys.exit(0)` after printing the help or version information. If this is not desired (e.g. testing purposes), you can override the `terminate(exitState: Either[String, Unit])` method: ```scala // Overriding the termination handler to no-op. override def terminate(exitState: Either[String, Unit]): Unit = () ``` ### Rendering mode scopt 3.5.0 introduced rendering mode, and adopted two-column rendeing of the usage text by default. To switch back to the older one-column rendering override the `renderingMode` method: ```scala override def renderingMode = scopt.RenderingMode.OneColumn ``` This will output the sample usage as follows: ``` scopt 3.x Usage: scopt [update] [options] [...] -f | --foo foo is an integer property -o | --out out is a required file property --max:= maximum count for -j ,... | --jars ,... jars to include --kwargs k1=v1,k2=v2... other arguments --verbose verbose is a flag --help prints this usage text ... optional unbounded args some notes. Command: update [options] update is a command. -nk | --not-keepalive disable keepalive --xyz xyz is a boolean property ``` ### Mutable parsing Create a `scopt.OptionParser[Unit]` and customize it with the options you need, passing in functions to process each option or argument. Use `foreach` instead of `action`. ```scala val parser = new scopt.OptionParser[Unit]("scopt") { head("scopt", "3.x") opt[Int]('f', "foo").foreach( x => c = c.copy(foo = x) ). text("foo is an integer property") opt[File]('o', "out").required().valueName(""). foreach( x => c = c.copy(out = x) ).text("out is a required file property") opt[(String, Int)]("max").foreach( { case (k, v) => c = c.copy(libName = k, maxCount = v) }). validate( x => if (x._2 > 0) success else failure("Value must be >0") ). keyValueName("", ""). text("maximum count for ") opt[Unit]("verbose").foreach( _ => c = c.copy(verbose = true) ). text("verbose is a flag") opt[Unit]("debug").hidden().foreach( _ => c = c.copy(debug = true) ). text("this option is hidden in the usage text") help("help").text("prints this usage text") arg[File]("...").unbounded().optional(). foreach( x => c = c.copy(files = c.files :+ x) ). text("optional unbounded args") note("some notes.".newline) cmd("update").foreach( _ => c.copy(mode = "update") ). text("update is a command."). children( opt[Unit]("not-keepalive").abbr("nk"). foreach( _ => c.copy(keepalive = false) ).text("disable keepalive"), opt[Boolean]("xyz").foreach( x => c = c.copy(xyz = x) ).text("xyz is a boolean property"), opt[Unit]("debug-update").hidden(). foreach( _ => c = c.copy(debug = true) ). text("this option is hidden in the usage text") ) } if (parser.parse(args)) { // do stuff } else { // arguments are bad, usage message will have been displayed } ``` Building -------- sbt to build scopt. License ------- MIT License. Credits ------- - January 13, 2008: Aaron Harnly creates [aaronharnly/scala-options](https://github.com/aaronharnly/scala-options). - December 1, 2009: Tim Perrett introduces it [as a gist](http://gist.github.com/246481) on [Parsing command lines argument in a "scalaesque" way](http://www.scala-lang.org/node/4380). - January 10, 2010: James Strachan takes the code, adds usage text, sbt build, etc and creates [jstrachan/scopt](https://github.com/jstrachan/scopt), which is also mentioned in [Scala CLI Library?](http://www.scala-lang.org/node/4959). - March 4th, 2010: Eugene Yokota joins scopt project, improves usage text, and adds support for key=value option and argument list. - May 27, 2011: scopt 1.0.0 is released to scala-tools.org. - March 18, 2012: Eugene adds immutable parser, forks the project to [scopt/scopt](https://github.com/scopt/scopt), and releases scopt 2.0.0. - June 7, 2013: Eugene rewrites scopt from scratch for polymorphic options, and releases scopt 3.0.0. Changes ------- See [notes](https://github.com/scopt/scopt/tree/scopt3/notes). scopt-3.5.0/build.sbt000066400000000000000000000023651272715210000144630ustar00rootroot00000000000000def v: String = "3.5.0" lazy val root = (project in file(".")). settings( inThisBuild(Seq( version := v, organization := "com.github.scopt", scalaVersion := "2.11.8", crossScalaVersions := Seq("2.11.8", "2.10.6", "2.12.0-M4"), homepage := Some(url("https://github.com/scopt/scopt")), licenses := Seq("MIT License" -> url("http://www.opensource.org/licenses/mit-license.php")) )), name := "scopt", // site // to preview, preview-site // to push, ghpages-push-site site.settings, site.includeScaladoc(s"$v/api"), ghpages.settings, git.remoteRepo := "git@github.com:scopt/scopt.git", description := """a command line options parsing library""", libraryDependencies ++= { scalaVersion.value match { case x if x startsWith "2.10." => List("org.specs2" %% "specs2" % "2.3.3" % "test") case x if x startsWith "2.11." => List("org.specs2" %% "specs2" % "2.3.11" % "test") case _ => Nil } }, scalacOptions ++= Seq("-language:existentials"), resolvers += "sonatype-public" at "https://oss.sonatype.org/content/repositories/public", // scaladoc fix unmanagedClasspath in Compile += Attributed.blank(new java.io.File("doesnotexist")) ) scopt-3.5.0/migration.md000066400000000000000000000123271272715210000151640ustar00rootroot00000000000000Migration from scopt 2.x ======================== Here's an example of scopt 2.x immutable parser: ```scala case class Config(foo: Int = 0) val parser = new scopt.immutable.OptionParser[Config]("scopt", "2.x") { def options = Seq( intOpt("f", "foo", "foo is an integer property") { (v: Int, c: Config) => c.copy(foo = v) }, help(None, "version", "display this message"), arglist("...", "input schema to be converted") { (x: String, c: Config) => c.copy(files = c.files :+ (new File(x))) } ) } // parser.parse returns Option[C] parser.parse(args, Config()) map { config => println(config.foo) } getOrElse { // arguments are bad, usage message will have been displayed } ``` ### step 0 The case class and `parser.parse` part stay the same. So we'll look at the parser part. ### step 1 Create `new scopt.OptionParser[Config]` instead of `immutable` or `mutable`. ### step 2 `def options = Seq()` is no longer needed. ```scala val parser = new scopt.OptionParser[Config]("scopt", "2.x") { intOpt("f", "foo", "foo is an integer property") { (v: Int, c: Config) => c.copy(foo = v) } help(None, "version", "display this message") arglist("...", "input schema to be converted") { (x: String, c: Config) => c.copy(files = c.files :+ (new File(x))) } } ``` ### step 3 Move out version number into `head` along with the product name. ```scala val parser = new scopt.OptionParser[Config]("scopt") { head("Very scopt", "3.x") intOpt("f", "foo", "foo is an integer property") { (v: Int, c: Config) => c.copy(foo = v) } help(None, "version", "display this message") arglist("...", "input schema to be converted") { (x: String, c: Config) => c.copy(files = c.files :+ (new File(x))) } } ``` ### step 4 Change `opt`, `intOpt`, `keyValueOpt` to `opt[String]`, `opt[Int]`, and `opt[(String, String)]` respectively. ```scala val parser = new scopt.OptionParser[Config]("scopt") { head("Very scopt", "3.x") opt[Int]('f', "foo", "foo is an integer property") { (v: Int, c: Config) => c.copy(foo = v) } help(None, "version", "display this message") arglist("...", "input schema to be converted") { (x: String, c: Config) => c.copy(files = c.files :+ (new File(x))) } } ``` ### step 5 Change `shortOpt` to a `Char`, or omit the parameter instead of passing `None`. ```scala val parser = new scopt.OptionParser[Config]("scopt") { head("Very scopt", "3.x") opt[Int]('f', "foo", "foo is an integer property") { (v: Int, c: Config) => c.copy(foo = v) } help("version", "display this message") arglist("...", "input schema to be converted") { (x: String, c: Config) => c.copy(files = c.files :+ (new File(x))) } } ``` ### step 6 Move descriptions out to `text` method. ```scala val parser = new scopt.OptionParser[Config]("scopt") { head("Very scopt", "3.x") opt[Int]('f', "foo") text("foo is an integer property") { (v: Int, c: Config) => c.copy(foo = v) } help("version") text("display this message") arglist("...") text("input schema to be converted") { (x: String, c: Config) => c.copy(files = c.files :+ (new File(x))) } } ``` ### step 7 Place callback closure in `action` method for immutable parsing. `foreach` for mutable parsing. Type annotations shouldn't be needed. ```scala val parser = new scopt.OptionParser[Config]("scopt") { head("Very scopt", "3.x") opt[Int]('f', "foo") text("foo is an integer property") action { (x, c) => c.copy(foo = x) } help("version") text("display this message") arglist("...") text("input schema to be converted") action { (x, c) => c.copy(files = c.files :+ (new File(x))) } } ``` ### step 8 There's now `version` method that prints out only the header text. Consider providing both `--help` and `--version`. ```scala val parser = new scopt.OptionParser[Config]("scopt") { head("Very scopt", "3.x") opt[Int]('f', "foo") text("foo is an integer property") action { (x, c) => c.copy(foo = x) } help("help") text("display this message") version("version") text("display version info") arglist("...") text("input schema to be converted") action { (x, c) => c.copy(files = c.files :+ (new File(x))) } } ``` ### step 9 Change both `arg` and `arglist` to `arg[String]`. Call `unbounded()` for `arglist`. ```scala val parser = new scopt.OptionParser[Config]("scopt") { head("Very scopt", "3.x") opt[Int]('f', "foo") text("foo is an integer property") action { (x, c) => c.copy(foo = x) } help("help") text("display this message") version("version") text("display version info") arg[String]("...") unbounded() text("input schema to be converted") action { (x, c) => c.copy(files = c.files :+ (new File(x))) } } ``` ### step 10 scopt now supports `java.io.File`, `java.util.Calendar`, `java.net.URI`, etc. ```scala val parser = new scopt.OptionParser[Config]("scopt") { head("Very scopt", "3.x") opt[Int]('f', "foo") text("foo is an integer property") action { (x, c) => c.copy(foo = x) } help("help") text("display this message") version("version") text("display version info") arg[File]("...") unbounded() text("input schema to be converted") action { (x, c) => c.copy(files = c.files :+ x) } } ``` scopt-3.5.0/notes/000077500000000000000000000000001272715210000137745ustar00rootroot00000000000000scopt-3.5.0/notes/1.0.0.markdown000066400000000000000000000031321272715210000161730ustar00rootroot00000000000000## hello, world This is the initial release of scopt, a little command line parsing library. You can customize an *OptionParser* by passing in functions to process each option or argument. val parser = new OptionParser("scopt") { intOpt("f", "foo", "foo is an integer property", {v: Int => config.foo = v}) opt("o", "output", "", "output is a string property", {v: String => config.bar = v}) booleanOpt("xyz", "xyz is a boolean property", {v: Boolean => config.xyz = v}) keyValueOpt("l", "lib", "", "", "load library ", {(key: String, value: String) => { config.libname = key; config.libfile = value } }) arg("", " is an argument", {v: String => config.whatnot = v}) // arglist("...", "arglist allows variable number of arguments", // {v: String => config.files = (v :: config.files).reverse }) } if (parser.parse(args)) { // do stuff } else { // arguments are bad, usage message will have been displayed } It handles various kinds of options such as optional, required, and key-value. From the above, it also automatically generates the usage text as follows: Usage: scopt [options] -f | --foo foo is an integer property -o | --output output is a string property --xyz xyz is a boolean property -l:= | --lib:= load library is an argument scopt-3.5.0/notes/1.1.0.markdown000066400000000000000000000001001272715210000161640ustar00rootroot00000000000000- Adds `argOpt` and `arglistOpt` method for optional arguments. scopt-3.5.0/notes/1.1.2.markdown000066400000000000000000000000671272715210000162020ustar00rootroot00000000000000- Cross built against Scala 2.9.1, 2.9.0-1, and 2.8.1. scopt-3.5.0/notes/1.1.3.markdown000066400000000000000000000000331272715210000161740ustar00rootroot00000000000000- deleted log4j.properties scopt-3.5.0/notes/2.0.0.markdown000066400000000000000000000024011272715210000161720ustar00rootroot00000000000000## immutable parser In addition to the mutable parser from scopt 1.x, 2.0.0 adds an immutable parser. val parser = new scopt.immutable.OptionParser[Config]("scopt", "2.x") { def options = Seq( intOpt("f", "foo", "foo is an integer property") { (v: Int, c: Config) => c.copy(foo = v) }, opt("o", "output", "output") { (v: String, c: Config) => c.copy(bar = v) }, booleanOpt("xyz", "xyz is a boolean property") { (v: Boolean, c: Config) => c.copy(xyz = v) }, keyValueOpt("l", "lib", "", "", "load library ") { (key: String, value: String, c: Config) => c.copy(libname = key, libfile = value) }, keyIntValueOpt(None, "max", "", "", "maximum count for ") { (key: String, value: Int, c: Config) => c.copy(maxlibname = key, maxcount = value) }, arg("", "some argument") { (v: String, c: Config) => c.copy(whatnot = v) } ) } // parser.parse returns Option[C] parser.parse(args, Config()) map { config => // do stuff } getOrElse { // arguments are bad, usage message will have been displayed } Instead of updating the config object in-place, the immutable parser expects call back functions that takes a config object and returns a new one. scopt-3.5.0/notes/2.0.1.markdown000066400000000000000000000001121272715210000161700ustar00rootroot00000000000000- Fixes Unit opt. [#1][1] [1]: https://github.com/scopt/scopt/issues/1 scopt-3.5.0/notes/2.1.0.markdown000066400000000000000000000007251272715210000162020ustar00rootroot00000000000000- Fixes the signature of `opt(String, String, String)`. [#2][2] - Fixes `help` and `flag` methods. [#3][3]/[#4][4];[#6][6] contributed by [@kxbmap][@kxbmap] - Cross publishes to Scala 2.8.1, 2.8.2, 2.9.0-1, 2.9.1, and 2.9.2 as a good citizen. [2]: https://github.com/scopt/scopt/issues/2 [3]: https://github.com/scopt/scopt/issues/3 [4]: https://github.com/scopt/scopt/issues/4 [6]: https://github.com/scopt/scopt/pull/6 [@kxbmap]: https://github.com/kxbmap scopt-3.5.0/notes/3.0.0.markdown000066400000000000000000000026071272715210000162030ustar00rootroot00000000000000## all new scopt 3.0 scopt 3.0 was rewritten from scratch to clean up the API: val parser = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") opt[Int]('f', "foo") action { (x, c) => c.copy(foo = x) } text("foo is an integer property") arg[File]("...") unbounded() optional() action { (x, c) => c.copy(files = c.files :+ x) } text("optional unbounded args") } ### polymorphic opt/arg Both `opt[A]` and `arg[A]` take a type parameter of `A: Read`. Out of the box, `Unit`, `Int`, `Long`, `Double`, `String`, `Boolean`, `BigInt`, `BigDecimal`, `java.io.File`, `java.net.URI`, `java.util.Calendar` are supported. Key=value parameters are expressed as pair `(A1, A2)`. ### occurrences and custom validation Occurrences can be specified using the fluent interface: opt[File]('o', "out") required() arg[File]("...") optional() unbounded() Custom validation logic: opt[Int]('f', "foo") validate { x => if (x > 0) success else failure("Option --foo must be >0") } ### commands scopt3 supports multiple levels of command structure: cmd("backend") text("commands to manipulate backends:\n") action { (x, c) => c.copy(flag = true) } children { cmd("update") children { arg[String]("") action { (x, c) => c.copy(a = x) } } } See [scopt](https://github.com/scopt/scopt) for more details. scopt-3.5.0/notes/3.1.0.markdown000066400000000000000000000017251272715210000162040ustar00rootroot00000000000000### string short options scopt 3.0 changed short options from `String` to `Char` to enforce all short options to be a single character. In scopt 3.1 `opt` still uses `Char`, but using `abbr("nk")` it supports `String` short options: val parser = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") opt[Unit]('n', "netrc") opt[Unit]('k', "keepalive") action { (x, c) => c.copy(keepalive= true) } opt[Unit]("no-keepalive") abbr("nk") action { (x, c) => c.copy(keepalive = false) } } Grouped options are parsed greedily. For example, in the above `-nk` is parsed as `--no-keepalive`; and `-nknk` is parsed as `--no-keepalive --netrc --keepalive`. [#19][19] ### bug fixes and minor enhancements - Uses `Console.err` instead of `System.err` [#18][18] contributed by [@kxbmap][@kxbmap]. [18]: https://github.com/scopt/scopt/pull/18 [19]: https://github.com/scopt/scopt/issues/19 [@kxbmap]: https://github.com/kxbmap scopt-3.5.0/notes/3.2.0.markdown000066400000000000000000000032071272715210000162020ustar00rootroot00000000000000 [21]: https://github.com/scopt/scopt/pull/21 [22]: https://github.com/scopt/scopt/issues/22 [24]: https://github.com/scopt/scopt/issues/24 [27]: https://github.com/scopt/scopt/issues/27 [@zero-sum]: https://github.com/zero-sum [@tksk]: https://github.com/tksk [@Ceilican]: https://github.com/Ceilican ## breaking changes ### shorter error message Instead of printing a wall of usage text on error, if `help("help")` option is defined, scopt 3.2.0 prints just the error message and suggests to "Try --help for more information." This behavior can be reverted by overriding `showUsageOnError`. [#22][22] requested by [@tksk][@tksk]. ### usage text prints to `Console.out` Usage text will print to `Console.out` instead of `Console.err` when invoked as `--help`. [#24][24] reported by [@Ceilican][@Ceilican]. ## new features ### hidden options opt can now be `hidden()`. opt[Unit]("debug") hidden() action { (_, c) => c.copy(debug = true) } text("this option is hidden in the usage text") [#21][21] contributed by [@tksk][@tksk]. ### check configuration To check consistency among the provided `opt` values, scopt 3.2.0 introduces `checkConfig`. val parser = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") opt[Unit]('k', "keepalive") action { (x, c) => c.copy(keepalive= true) } opt[Boolean]("xyz") action { (x, c) => c.copy(xyz = x) } text("xyz is a boolean property"), checkConfig { c => if (c.keepalive && c.xyz) failure("xyz cannot keep alive") else success } help("help") text("prints this usage text") } [#27][27] requested by [@zero-sum][@zero-sum]. scopt-3.5.0/notes/3.3.0.markdown000066400000000000000000000014401272715210000162000ustar00rootroot00000000000000 [56]: https://github.com/scopt/scopt/pull/56 [@melrief]: https://github.com/melrief ## comma-separated values scopt 3.3.0 adds support for comma-separated values that map to `Seq[A]` and `Map[K, V]` (where `A`, `K` and `V` are instance of `Read`). - A `Seq[File]` accepts a string containing comma-separated values such as `--jars foo.jar,bar.jar` - A `Map[String, String]` accepts a string containing comma-separated pairs like `--kwargs key1=val1,key2=val2` Here's how they can be used: opt[Seq[File]]('j', "jars") valueName(",...") action { (x,c) => c.copy(jars = x) } text("jars to include") opt[Map[String,String]]("kwargs") valueName("k1=v1,k2=v2...") action { (x, c) => c.copy(kwargs = x) } text("other arguments") [#56][56] by [@melrief][@melrief]. scopt-3.5.0/notes/3.4.0.markdown000066400000000000000000000033131272715210000162020ustar00rootroot00000000000000 [70]: https://github.com/scopt/scopt/pull/70 [74]: https://github.com/scopt/scopt/pull/74 [79]: https://github.com/scopt/scopt/pull/79 [83]: https://github.com/scopt/scopt/pull/83 [86]: https://github.com/scopt/scopt/pull/86 [87]: https://github.com/scopt/scopt/pull/87 [@sonenko]: https://github.com/sonenko [@alexanderfefelov]: https://github.com/alexanderfefelov [@metasim]: https://github.com/metasim [@billonahill]: https://github.com/billonahill [@serejja]: https://github.com/serejja [@maizy]: https://github.com/maizy ### support for optional equal sign Given a parser like this: val intParser1 = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") opt[Int]('f', "foo") action { (x, c) => c.copy(intValue = x) } help("help") } scopt 3.4.0 accepts all of the following options: - `--foo 1` - `--foo:1` - `--foo=1` This was contributed as [#87][87] by [@maizy][@maizy]. ### terminate scopt 3.4.0 adds a termination hanlder called `terminate(exitState: Either[String, Unit])`. Override this method to prevent scopt from calling `sys.exit(0)` on `--help` or `--version`. This was contributed as [#74][74] by [@metasim][@metasim]. ### other minor fixes and enhancements - Adds `Read[Seq[(K,V)]]`, which parses `key=1,key=2` as `List("key" -> "1","key" -> "2")`. [#70][70] by [@sonenko][@sonenko] - Adds `Read[InetAddress]`, which parses an IP address using `java.net.InetAddress.getByName`. [#79][79] by [@alexanderfefelov][@alexanderfefelov] - Adds `Read[Duration]`, which parses `30s` as `Duration("30s")`. [#86][86] by [@serejja][@serejja] - Increases the limit of `unbounded()` args. [#83][83] by [@billonahill][@billonahill] - Scala 2.9.x support is dropped. scopt-3.5.0/notes/3.5.0.markdown000066400000000000000000000022041272715210000162010ustar00rootroot00000000000000 [@Jibbers42]: https://github.com/Jibbers42 [109]: https://github.com/scopt/scopt/pull/109 ## breaking changes ### two-column rendering scopt 3.5.0 introduces two-column rendering for the usage text, which is enabled by default: scopt 3.x Usage: scopt [update] [options] [...] -f, --foo foo is an integer property -o, --out out is a required file property Command: update [options] update is a command. --xyz xyz is a boolean property This is a bit more compact compared to the previous (one-column) rendering that looked like this: scopt 3.x Usage: scopt [update] [options] [...] -f | --foo foo is an integer property -o | --out out is a required file property Command: update [options] update is a command. --xyz xyz is a boolean property You can switch back to the one-column rendering as follows: override def renderingMode = scopt.RenderingMode.OneColumn This feature was contributed by [@Jibbers42][@Jibbers42] as [#109][109]. scopt-3.5.0/notes/about.markdown000066400000000000000000000001321272715210000166460ustar00rootroot00000000000000[scopt](https://github.com/scopt/scopt) is a little command line options parsing library. scopt-3.5.0/project/000077500000000000000000000000001272715210000143125ustar00rootroot00000000000000scopt-3.5.0/project/build.properties000066400000000000000000000000231272715210000175220ustar00rootroot00000000000000sbt.version=0.13.9 scopt-3.5.0/project/site.sbt000066400000000000000000000005571272715210000157770ustar00rootroot00000000000000resolvers += "jgit-repo" at "http://download.eclipse.org/jgit/maven" addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "0.7.1") addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.5.2") ivyXML := scopt-3.5.0/sonatype.sbt000066400000000000000000000013561272715210000152250ustar00rootroot00000000000000 pomExtra := ( git@github.com:scopt/scopt.git scm:git:git@github.com:scopt/scopt.git eed3si9n Eugene Yokota http://eed3si9n.com ) // --- Sonatype settings --- publishMavenStyle := true publishArtifact in (Compile, packageBin) := true publishArtifact in Test := false publishTo <<= version { (v: String) => val nexus = "https://oss.sonatype.org/" if (v.trim.endsWith("SNAPSHOT")) Some("snapshots" at nexus + "content/repositories/snapshots") else Some("releases" at nexus + "service/local/staging/deploy/maven2") } pomIncludeRepository := { x => false } scopt-3.5.0/src/000077500000000000000000000000001272715210000134335ustar00rootroot00000000000000scopt-3.5.0/src/main/000077500000000000000000000000001272715210000143575ustar00rootroot00000000000000scopt-3.5.0/src/main/ls/000077500000000000000000000000001272715210000147755ustar00rootroot00000000000000scopt-3.5.0/src/main/ls/1.1.3.json000066400000000000000000000007031272715210000163300ustar00rootroot00000000000000 { "organization":"com.github.scopt", "name":"scopt", "version":"1.1.3", "description":"a command line options parsing library", "site":"", "tags":["cli","command-line","parsing","parser"], "docs":"", "licenses": [{ "name": "MIT License", "url": "http://www.opensource.org/licenses/mit-license.php" }], "resolvers": ["http://scala-tools.org/repo-releases"], "dependencies": [], "scalas": ["2.9.1","2.9.0-1","2.8.1"], "sbt": false }scopt-3.5.0/src/main/ls/2.0.0.json000066400000000000000000000007411272715210000163270ustar00rootroot00000000000000 { "organization":"com.github.scopt", "name":"scopt", "version":"2.0.0", "description":"a command line options parsing library", "site":"https://github.com/scopt/scopt", "tags":["cli","command-line","parsing","parser"], "docs":"", "licenses": [{ "name": "MIT License", "url": "http://www.opensource.org/licenses/mit-license.php" }], "resolvers": ["http://scala-tools.org/repo-releases"], "dependencies": [], "scalas": ["2.9.1","2.9.0-1","2.8.1"], "sbt": false }scopt-3.5.0/src/main/ls/2.0.1.json000066400000000000000000000007611272715210000163320ustar00rootroot00000000000000 { "organization":"com.github.scopt", "name":"scopt", "version":"2.0.1", "description":"a command line options parsing library", "site":"https://github.com/scopt/scopt", "tags":["cli","command-line","parsing","parser"], "docs":"", "licenses": [{ "name": "MIT License", "url": "http://www.opensource.org/licenses/mit-license.php" }], "resolvers": ["https://oss.sonatype.org/content/repositories/public"], "dependencies": [], "scalas": ["2.9.1","2.9.0-1","2.8.1"], "sbt": false }scopt-3.5.0/src/main/ls/2.1.0.json000066400000000000000000000010011272715210000163160ustar00rootroot00000000000000 { "organization":"com.github.scopt", "name":"scopt", "version":"2.1.0", "description":"a command line options parsing library", "site":"https://github.com/scopt/scopt", "tags":["cli","command-line","parsing","parser"], "docs":"", "licenses": [{ "name": "MIT License", "url": "http://www.opensource.org/licenses/mit-license.php" }], "resolvers": ["https://oss.sonatype.org/content/repositories/public"], "dependencies": [], "scalas": ["2.9.2","2.9.1","2.9.0-1","2.8.1","2.8.2"], "sbt": false }scopt-3.5.0/src/main/ls/3.0.0.json000066400000000000000000000010511272715210000163230ustar00rootroot00000000000000{ "organization" : "com.github.scopt", "name" : "scopt", "version" : "3.0.0", "description" : "a command line options parsing library", "site" : "https://github.com/scopt/scopt", "tags" : [ "cli", "command-line", "parsing", "parser" ], "docs" : "", "resolvers" : [ "https://oss.sonatype.org/content/repositories/public" ], "dependencies" : [ ], "scalas" : [ "2.10.1", "2.9.1", "2.9.2", "2.9.3" ], "licenses" : [ { "name" : "MIT License", "url" : "http://www.opensource.org/licenses/mit-license.php" } ], "sbt" : false }scopt-3.5.0/src/main/ls/3.1.0.json000066400000000000000000000010511272715210000163240ustar00rootroot00000000000000{ "organization" : "com.github.scopt", "name" : "scopt", "version" : "3.1.0", "description" : "a command line options parsing library", "site" : "https://github.com/scopt/scopt", "tags" : [ "cli", "command-line", "parsing", "parser" ], "docs" : "", "resolvers" : [ "https://oss.sonatype.org/content/repositories/public" ], "dependencies" : [ ], "scalas" : [ "2.10.1", "2.9.1", "2.9.2", "2.9.3" ], "licenses" : [ { "name" : "MIT License", "url" : "http://www.opensource.org/licenses/mit-license.php" } ], "sbt" : false }scopt-3.5.0/src/main/ls/3.2.0.json000066400000000000000000000010511272715210000163250ustar00rootroot00000000000000{ "organization" : "com.github.scopt", "name" : "scopt", "version" : "3.2.0", "description" : "a command line options parsing library", "site" : "https://github.com/scopt/scopt", "tags" : [ "cli", "command-line", "parsing", "parser" ], "docs" : "", "resolvers" : [ "https://oss.sonatype.org/content/repositories/public" ], "dependencies" : [ ], "scalas" : [ "2.10.3", "2.9.1", "2.9.2", "2.9.3" ], "licenses" : [ { "name" : "MIT License", "url" : "http://www.opensource.org/licenses/mit-license.php" } ], "sbt" : false }scopt-3.5.0/src/main/ls/3.3.0.json000066400000000000000000000010631272715210000163310ustar00rootroot00000000000000{ "organization" : "com.github.scopt", "name" : "scopt", "version" : "3.3.0", "description" : "a command line options parsing library", "site" : "https://github.com/scopt/scopt", "tags" : [ "cli", "command-line", "parsing", "parser" ], "docs" : "", "resolvers" : [ "https://oss.sonatype.org/content/repositories/public" ], "dependencies" : [ ], "scalas" : [ "2.11.4", "2.10.4", "2.9.1", "2.9.2", "2.9.3" ], "licenses" : [ { "name" : "MIT License", "url" : "http://www.opensource.org/licenses/mit-license.php" } ], "sbt" : false }scopt-3.5.0/src/main/scala/000077500000000000000000000000001272715210000154425ustar00rootroot00000000000000scopt-3.5.0/src/main/scala/scopt/000077500000000000000000000000001272715210000165725ustar00rootroot00000000000000scopt-3.5.0/src/main/scala/scopt/options.scala000077500000000000000000000711021272715210000212760ustar00rootroot00000000000000package scopt import java.net.UnknownHostException import java.text.ParseException import collection.mutable.{ListBuffer, ListMap} trait Read[A] { self => def arity: Int def tokensToRead: Int = if (arity == 0) 0 else 1 def reads: String => A def map[B](f: A => B): Read[B] = new Read[B] { val arity = self.arity val reads = self.reads andThen f } } object Read { import java.util.{Locale, Calendar, GregorianCalendar} import java.text.SimpleDateFormat import java.io.File import java.net.URI import java.net.InetAddress import scala.concurrent.duration.Duration def reads[A](f: String => A): Read[A] = new Read[A] { val arity = 1 val reads = f } implicit val intRead: Read[Int] = reads { _.toInt } implicit val stringRead: Read[String] = reads { identity } implicit val doubleRead: Read[Double] = reads { _.toDouble } implicit val booleanRead: Read[Boolean] = reads { _.toLowerCase match { case "true" => true case "false" => false case "yes" => true case "no" => false case "1" => true case "0" => false case s => throw new IllegalArgumentException("'" + s + "' is not a boolean.") }} implicit val longRead: Read[Long] = reads { _.toLong } implicit val bigIntRead: Read[BigInt] = reads { BigInt(_) } implicit val bigDecimalRead: Read[BigDecimal] = reads { BigDecimal(_) } implicit val yyyymmdddRead: Read[Calendar] = calendarRead("yyyy-MM-dd") def calendarRead(pattern: String): Read[Calendar] = calendarRead(pattern, Locale.getDefault) def calendarRead(pattern: String, locale: Locale): Read[Calendar] = reads { s => val fmt = new SimpleDateFormat(pattern) val c = new GregorianCalendar c.setTime(fmt.parse(s)) c } implicit val fileRead: Read[File] = reads { new File(_) } implicit val uriRead: Read[URI] = reads { new URI(_) } implicit val inetAddress: Read[InetAddress] = reads { InetAddress.getByName(_) } implicit val durationRead: Read[Duration] = reads { try { Duration(_) } catch { case e: NumberFormatException => throw new ParseException(e.getMessage, -1) }} implicit def tupleRead[A1: Read, A2: Read]: Read[(A1, A2)] = new Read[(A1, A2)] { val arity = 2 val reads = { (s: String) => splitKeyValue(s) match { case (k, v) => implicitly[Read[A1]].reads(k) -> implicitly[Read[A2]].reads(v) } } } private def splitKeyValue(s: String): (String, String) = s.indexOf('=') match { case -1 => throw new IllegalArgumentException("Expected a key=value pair") case n: Int => (s.slice(0, n), s.slice(n + 1, s.length)) } implicit val unitRead: Read[Unit] = new Read[Unit] { val arity = 0 val reads = { (s: String) => () } } val sep = "," // reads("1,2,3,4,5") == Seq(1,2,3,4,5) implicit def seqRead[A: Read]: Read[Seq[A]] = reads { (s: String) => s.split(sep).map(implicitly[Read[A]].reads) } // reads("1=false,2=true") == Map(1 -> false, 2 -> true) implicit def mapRead[K: Read, V: Read]: Read[Map[K,V]] = reads { (s: String) => s.split(sep).map(implicitly[Read[(K,V)]].reads).toMap } // reads("1=false,1=true") == List((1 -> false), (1 -> true)) implicit def seqTupleRead[K: Read, V: Read]: Read[Seq[(K,V)]] = reads { (s: String) => s.split(sep).map(implicitly[Read[(K,V)]].reads).toSeq } } trait Zero[A] { def zero: A } object Zero { def zero[A](f: => A): Zero[A] = new Zero[A] { val zero = f } implicit val intZero: Zero[Int] = zero(0) implicit val unitZero: Zero[Unit] = zero(()) } object Validation { def validateValue[A](vs: Seq[A => Either[String, Unit]])(value: A): Either[Seq[String], Unit] = { val results = vs map {_.apply(value)} (OptionDef.makeSuccess[Seq[String]] /: results) { (acc, r) => (acc match { case Right(_) => Seq[String]() case Left(xs) => xs }) ++ (r match { case Right(_) => Seq[String]() case Left(x) => Seq[String](x) }) match { case Seq() => acc case xs => Left(xs) } } } } trait RenderingMode object RenderingMode { case object OneColumn extends RenderingMode case object TwoColumns extends RenderingMode } private[scopt] sealed trait OptionDefKind {} private[scopt] case object Opt extends OptionDefKind private[scopt] case object Note extends OptionDefKind private[scopt] case object Arg extends OptionDefKind private[scopt] case object Cmd extends OptionDefKind private[scopt] case object Head extends OptionDefKind private[scopt] case object Check extends OptionDefKind /** scopt.immutable.OptionParser is instantiated within your object, * set up by an (ordered) sequence of invocations of * the various builder methods such as * opt method or * arg method. * {{{ * val parser = new scopt.OptionParser[Config]("scopt") { * head("scopt", "3.x") * * opt[Int]('f', "foo").action( (x, c) => * c.copy(foo = x) ).text("foo is an integer property") * * opt[File]('o', "out").required().valueName(""). * action( (x, c) => c.copy(out = x) ). * text("out is a required file property") * * opt[(String, Int)]("max").action({ * case ((k, v), c) => c.copy(libName = k, maxCount = v) }). * validate( x => * if (x._2 > 0) success * else failure("Value must be >0") ). * keyValueName("", ""). * text("maximum count for ") * * opt[Seq[File]]('j', "jars").valueName(",...").action( (x,c) => * c.copy(jars = x) ).text("jars to include") * * opt[Map[String,String]]("kwargs").valueName("k1=v1,k2=v2...").action( (x, c) => * c.copy(kwargs = x) ).text("other arguments") * * opt[Unit]("verbose").action( (_, c) => * c.copy(verbose = true) ).text("verbose is a flag") * * opt[Unit]("debug").hidden().action( (_, c) => * c.copy(debug = true) ).text("this option is hidden in the usage text") * * help("help").text("prints this usage text") * * arg[File]("...").unbounded().optional().action( (x, c) => * c.copy(files = c.files :+ x) ).text("optional unbounded args") * * note("some notes.".newline) * * cmd("update").action( (_, c) => c.copy(mode = "update") ). * text("update is a command."). * children( * opt[Unit]("not-keepalive").abbr("nk").action( (_, c) => * c.copy(keepalive = false) ).text("disable keepalive"), * opt[Boolean]("xyz").action( (x, c) => * c.copy(xyz = x) ).text("xyz is a boolean property"), * opt[Unit]("debug-update").hidden().action( (_, c) => * c.copy(debug = true) ).text("this option is hidden in the usage text"), * checkConfig( c => * if (c.keepalive && c.xyz) failure("xyz cannot keep alive") * else success ) * ) * } * * // parser.parse returns Option[C] * parser.parse(args, Config()) match { * case Some(config) => * // do stuff * * case None => * // arguments are bad, error message will have been displayed * } * }}} */ abstract case class OptionParser[C](programName: String) { protected val options = new ListBuffer[OptionDef[_, C]] protected val helpOptions = new ListBuffer[OptionDef[_, C]] def errorOnUnknownArgument: Boolean = true def showUsageOnError: Boolean = helpOptions.isEmpty def renderingMode: RenderingMode = RenderingMode.TwoColumns def terminate(exitState: Either[String, Unit]): Unit = exitState match { case Left(_) => sys.exit(1) case Right(_) => sys.exit(0) } def reportError(msg: String): Unit = { Console.err.println("Error: " + msg) } def reportWarning(msg: String): Unit = { Console.err.println("Warning: " + msg) } def showTryHelp(): Unit = { def oxford(xs: List[String]): String = xs match { case a :: b :: Nil => a + " or " + b case _ => (xs.dropRight(2) :+ xs.takeRight(2).mkString(", or ")).mkString(", ") } Console.err.println("Try " + oxford(helpOptions.toList map {_.fullName}) + " for more information.") } /** adds usage text. */ def head(xs: String*): OptionDef[Unit, C] = makeDef[Unit](Head, "") text(xs.mkString(" ")) /** adds an option invoked by `--name x`. * @param name name of the option */ def opt[A: Read](name: String): OptionDef[A, C] = makeDef(Opt, name) /** adds an option invoked by `-x value` or `--name value`. * @param x name of the short option * @param name name of the option */ def opt[A: Read](x: Char, name: String): OptionDef[A, C] = opt[A](name) abbr(x.toString) /** adds usage text. */ def note(x: String): OptionDef[Unit, C] = makeDef[Unit](Note, "") text(x) /** adds an argument invoked by an option without `-` or `--`. * @param name name in the usage text */ def arg[A: Read](name: String): OptionDef[A, C] = makeDef(Arg, name) required() /** adds a command invoked by an option without `-` or `--`. * @param name name of the command */ def cmd(name: String): OptionDef[Unit, C] = makeDef[Unit](Cmd, name) /** adds an option invoked by `--name` that displays usage text and exits. * @param name name of the option */ def help(name: String): OptionDef[Unit, C] = { val o = opt[Unit](name) action { (x, c) => showUsage() terminate(Right(())) c } helpOptions += o o } /** adds an option invoked by `--name` that displays header text and exits. * @param name name of the option */ def version(name: String): OptionDef[Unit, C] = opt[Unit](name) action { (x, c) => showHeader() terminate(Right(())) c } /** adds final check. */ def checkConfig(f: C => Either[String, Unit]): OptionDef[Unit, C] = makeDef[Unit](Check, "") validateConfig(f) def showHeader() { Console.out.println(header) } def header: String = { import OptionDef._ (heads map {_.usage}).mkString(NL) } def showUsage(): Unit = { Console.out.println(usage) } def showUsageAsError(): Unit = { Console.err.println(usage) } def usage: String = renderUsage(renderingMode) def renderUsage(mode: RenderingMode): String = mode match { case RenderingMode.OneColumn => renderOneColumnUsage case RenderingMode.TwoColumns => renderTwoColumnsUsage } def renderOneColumnUsage: String = { import OptionDef._ val descriptions = optionsForRender map {_.usage} (if (header == "") "" else header + NL) + "Usage: " + usageExample + NLNL + descriptions.mkString(NL) } def renderTwoColumnsUsage: String = { import OptionDef._ val xs = optionsForRender val descriptions = { val col1Len = math.min(column1MaxLength, (xs map {_.usageColumn1.length + WW.length}).max) xs map {_.usageTwoColumn(col1Len)} } (if (header == "") "" else header + NL) + "Usage: " + usageExample + NLNL + descriptions.mkString(NL) } def optionsForRender: List[OptionDef[_, C]] = { val unsorted = options filter { o => o.kind != Head && o.kind != Check && !o.isHidden } val (unseen, xs) = unsorted partition {_.hasParent} match { case (p, np) => (ListBuffer() ++ p, ListBuffer() ++ np) } while (!unseen.isEmpty) { for { x <- xs } { val cs = unseen filter {_.getParentId == Some(x.id)} unseen --= cs xs.insertAll((xs indexOf x) + 1, cs) } } xs.toList } def usageExample: String = commandExample(None) private[scopt] def commandExample(cmd: Option[OptionDef[_, C]]): String = { val text = new ListBuffer[String]() text += cmd map {commandName} getOrElse programName val parentId = cmd map {_.id} val cs = commands filter {_.getParentId == parentId} if (cs.nonEmpty) text += cs map {_.name} mkString("[", "|", "]") val os = options.toSeq filter { case x => x.kind == Opt && x.getParentId == parentId } val as = arguments filter {_.getParentId == parentId} if (os.nonEmpty) text += "[options]" if (cs exists { case x => arguments exists {_.getParentId == Some(x.id)}}) text += "..." else if (as.nonEmpty) text ++= as map {_.argName} text.mkString(" ") } private[scopt] def commandName(cmd: OptionDef[_, C]): String = (cmd.getParentId map { x => (commands find {_.id == x} map {commandName} getOrElse {""}) + " " } getOrElse {""}) + cmd.name /** call this to express success in custom validation. */ def success: Either[String, Unit] = OptionDef.makeSuccess[String] /** call this to express failure in custom validation. */ def failure(msg: String): Either[String, Unit] = Left(msg) protected def heads: Seq[OptionDef[_, C]] = options.toSeq filter {_.kind == Head} protected def nonArgs: Seq[OptionDef[_, C]] = options.toSeq filter { case x => x.kind == Opt || x.kind == Note } protected def arguments: Seq[OptionDef[_, C]] = options.toSeq filter {_.kind == Arg} protected def commands: Seq[OptionDef[_, C]] = options.toSeq filter {_.kind == Cmd} protected def checks: Seq[OptionDef[_, C]] = options.toSeq filter {_.kind == Check} protected def makeDef[A: Read](kind: OptionDefKind, name: String): OptionDef[A, C] = updateOption(new OptionDef[A, C](parser = this, kind = kind, name = name)) private[scopt] def updateOption[A: Read](option: OptionDef[A, C]): OptionDef[A, C] = { val idx = options indexWhere { _.id == option.id } if (idx > -1) options(idx) = option else options += option option } /** parses the given `args`. * @return `true` if successful, `false` otherwise */ def parse(args: Seq[String])(implicit ev: Zero[C]): Boolean = parse(args, ev.zero) match { case Some(x) => true case None => false } /** parses the given `args`. */ def parse(args: Seq[String], init: C): Option[C] = { var i = 0 val pendingOptions = ListBuffer() ++ (nonArgs filterNot {_.hasParent}) val pendingArgs = ListBuffer() ++ (arguments filterNot {_.hasParent}) val pendingCommands = ListBuffer() ++ (commands filterNot {_.hasParent}) val occurrences = ListMap[OptionDef[_, C], Int]().withDefaultValue(0) var _config: C = init var _error = false def pushChildren(opt: OptionDef[_, C]): Unit = { // commands are cleared to guarantee that it appears first pendingCommands.clear() pendingOptions insertAll (0, nonArgs filter { x => x.getParentId == Some(opt.id) && !pendingOptions.contains(x) }) pendingArgs insertAll (0, arguments filter { x => x.getParentId == Some(opt.id) && !pendingArgs.contains(x) }) pendingCommands insertAll (0, commands filter { x => x.getParentId == Some(opt.id) && !pendingCommands.contains(x) }) } def handleError(msg: String): Unit = { if (errorOnUnknownArgument) { _error = true reportError(msg) } else reportWarning(msg) } def handleArgument(opt: OptionDef[_, C], arg: String): Unit = { opt.applyArgument(arg, _config) match { case Right(c) => _config = c pushChildren(opt) case Left(xs) => _error = true xs foreach reportError } } def handleOccurrence(opt: OptionDef[_, C], pending: ListBuffer[OptionDef[_, C]]): Unit = { occurrences(opt) += 1 if (occurrences(opt) >= opt.getMaxOccurs) { pending -= opt } } def findCommand(cmd: String): Option[OptionDef[_, C]] = pendingCommands find {_.name == cmd} // greedy match def handleShortOptions(g0: String): Unit = { val gs = (0 to g0.size - 1).toSeq map { n => g0.substring(0, g0.size - n) } gs flatMap { g => pendingOptions map {(g, _)} } find { case (g, opt) => opt.shortOptTokens("-" + g) == 1 } match { case Some(p) => val (g, option) = p handleOccurrence(option, pendingOptions) handleArgument(option, "") if (g0.drop(g.size) != "") { handleShortOptions(g0 drop g.size) } case None => handleError("Unknown option " + "-" + g0) } } def handleChecks(c: C): Unit = { Validation.validateValue(checks flatMap {_.checks})(c) match { case Right(c) => // do nothing case Left(xs) => _error = true xs foreach reportError } } while (i < args.length) { pendingOptions find {_.tokensToRead(i, args) > 0} match { case Some(option) => handleOccurrence(option, pendingOptions) option(i, args) match { case Right(v) => handleArgument(option, v) case Left(outOfBounds) => handleError(outOfBounds) } // move index forward for gobbling if (option.tokensToRead(i, args) > 1) { i += option.tokensToRead(i, args) - 1 } // if case None => args(i) match { case arg if arg startsWith "--" => handleError("Unknown option " + arg) case arg if arg startsWith "-" => if (arg == "-") handleError("Unknown option " + arg) else handleShortOptions(arg drop 1) case arg if findCommand(arg).isDefined => val cmd = findCommand(arg).get handleOccurrence(cmd, pendingCommands) handleArgument(cmd, "") case arg if pendingArgs.isEmpty => handleError("Unknown argument '" + arg + "'") case arg => val first = pendingArgs.head handleOccurrence(first, pendingArgs) handleArgument(first, arg) } } i += 1 } (pendingOptions filter { opt => opt.getMinOccurs > occurrences(opt) }) foreach { opt => if (opt.getMinOccurs == 1) reportError("Missing " + opt.shortDescription) else reportError(opt.shortDescription.capitalize + " must be given " + opt.getMinOccurs + " times") _error = true } (pendingArgs filter { arg => arg.getMinOccurs > occurrences(arg) }) foreach { arg => if (arg.getMinOccurs == 1) reportError("Missing " + arg.shortDescription) else reportError(arg.shortDescription.capitalize + "' must be given " + arg.getMinOccurs + " times") _error = true } handleChecks(_config) if (_error) { if (showUsageOnError) showUsageAsError() else showTryHelp() None } else Some(_config) } } class OptionDef[A: Read, C]( _parser: OptionParser[C], _id: Int, _kind: OptionDefKind, _name: String, _shortOpt: Option[String], _keyName: Option[String], _valueName: Option[String], _desc: String, _action: (A, C) => C, _validations: Seq[A => Either[String, Unit]], _configValidations: Seq[C => Either[String, Unit]], _parentId: Option[Int], _minOccurs: Int, _maxOccurs: Int, _isHidden: Boolean) { import OptionDef._ def this(parser: OptionParser[C], kind: OptionDefKind, name: String) = this(_parser = parser, _id = OptionDef.generateId, _kind = kind, _name = name, _shortOpt = None, _keyName = None, _valueName = None, _desc = "", _action = { (a: A, c: C) => c }, _validations = Seq(), _configValidations = Seq(), _parentId = None, _minOccurs = 0, _maxOccurs = 1, _isHidden = false) private[scopt] def copy( _parser: OptionParser[C] = this._parser, _id: Int = this._id, _kind: OptionDefKind = this._kind, _name: String = this._name, _shortOpt: Option[String] = this._shortOpt, _keyName: Option[String] = this._keyName, _valueName: Option[String] = this._valueName, _desc: String = this._desc, _action: (A, C) => C = this._action, _validations: Seq[A => Either[String, Unit]] = this._validations, _configValidations: Seq[C => Either[String, Unit]] = this._configValidations, _parentId: Option[Int] = this._parentId, _minOccurs: Int = this._minOccurs, _maxOccurs: Int = this._maxOccurs, _isHidden: Boolean = this._isHidden): OptionDef[A, C] = new OptionDef(_parser = _parser, _id = _id, _kind = _kind, _name = _name, _shortOpt = _shortOpt, _keyName = _keyName, _valueName = _valueName, _desc = _desc, _action = _action, _validations = _validations, _configValidations = _configValidations, _parentId = _parentId, _minOccurs = _minOccurs, _maxOccurs = _maxOccurs, _isHidden = _isHidden) private[this] def read: Read[A] = implicitly[Read[A]] /** Adds a callback function. */ def action(f: (A, C) => C): OptionDef[A, C] = _parser.updateOption(copy(_action = (a: A, c: C) => { f(a, _action(a, c)) })) /** Adds a callback function. */ def foreach(f: A => Unit): OptionDef[A, C] = _parser.updateOption(copy(_action = (a: A, c: C) => { val c2 = _action(a, c) f(a) c2 })) override def toString: String = fullName /** Adds short option -x. */ def abbr(x: String): OptionDef[A, C] = _parser.updateOption(copy(_shortOpt = Some(x))) /** Requires the option to appear at least `n` times. */ def minOccurs(n: Int): OptionDef[A, C] = _parser.updateOption(copy(_minOccurs = n)) /** Requires the option to appear at least once. */ def required(): OptionDef[A, C] = minOccurs(1) /** Chanages the option to be optional. */ def optional(): OptionDef[A, C] = minOccurs(0) /** Allows the argument to appear at most `n` times. */ def maxOccurs(n: Int): OptionDef[A, C] = _parser.updateOption(copy(_maxOccurs = n)) /** Allows the argument to appear multiple times. */ def unbounded(): OptionDef[A, C] = maxOccurs(UNBOUNDED) /** Adds description in the usage text. */ def text(x: String): OptionDef[A, C] = _parser.updateOption(copy(_desc = x)) /** Adds value name used in the usage text. */ def valueName(x: String): OptionDef[A, C] = _parser.updateOption(copy(_valueName = Some(x))) /** Adds key name used in the usage text. */ def keyName(x: String): OptionDef[A, C] = _parser.updateOption(copy(_keyName = Some(x))) /** Adds key and value names used in the usage text. */ def keyValueName(k: String, v: String): OptionDef[A, C] = keyName(k) valueName(v) /** Adds custom validation. */ def validate(f: A => Either[String, Unit]) = _parser.updateOption(copy(_validations = _validations :+ f)) /** Hides the option in any usage text. */ def hidden(): OptionDef[A, C] = _parser.updateOption(copy(_isHidden = true)) private[scopt] def validateConfig(f: C => Either[String, Unit]) = _parser.updateOption(copy(_configValidations = _configValidations :+ f)) private[scopt] def parent(x: OptionDef[_, C]): OptionDef[A, C] = _parser.updateOption(copy(_parentId = Some(x.id))) /** Adds opt/arg under this command. */ def children(xs: OptionDef[_, C]*): OptionDef[A, C] = { xs foreach {_.parent(this)} this } private[scopt] val kind: OptionDefKind = _kind private[scopt] val id: Int = _id private[scopt] val name: String = _name private[scopt] def callback: (A, C) => C = _action private[scopt] def getMinOccurs: Int = _minOccurs private[scopt] def getMaxOccurs: Int = _maxOccurs private[scopt] def shortOptOrBlank: String = _shortOpt getOrElse("") private[scopt] def hasParent: Boolean = _parentId.isDefined private[scopt] def getParentId: Option[Int] = _parentId private[scopt] def isHidden: Boolean = _isHidden private[scopt] def checks: Seq[C => Either[String, Unit]] = _configValidations private[scopt] def applyArgument(arg: String, config: C): Either[Seq[String], C] = try { val x = read.reads(arg) Validation.validateValue(_validations)(x) match { case Right(_) => Right(callback(x, config)) case Left(xs) => Left(xs) } } catch { case e: NumberFormatException => Left(Seq(shortDescription.capitalize + " expects a number but was given '" + arg + "'")) case e: UnknownHostException => Left(Seq(shortDescription.capitalize + " expects a host name or an IP address but was given '" + arg + "' which is invalid")) case e: ParseException => Left(Seq(shortDescription.capitalize + " expects a Scala duration but was given '" + arg + "'")) case e: Throwable => Left(Seq(shortDescription.capitalize + " failed when given '" + arg + "'. " + e.getMessage)) } // number of tokens to read: 0 for no match, 2 for "--foo 1", 1 for "--foo:1" private[scopt] def shortOptTokens(arg: String): Int = _shortOpt match { case Some(c) if arg == "-" + shortOptOrBlank => 1 + read.tokensToRead case Some(c) if arg startsWith ("-" + shortOptOrBlank + ":") => 1 case Some(c) if arg startsWith ("-" + shortOptOrBlank + "=") => 1 case _ => 0 } private[scopt] def longOptTokens(arg: String): Int = if (arg == fullName) 1 + read.tokensToRead else if ((arg startsWith (fullName + ":")) || (arg startsWith (fullName + "="))) 1 else 0 private[scopt] def tokensToRead(i: Int, args: Seq[String]): Int = if (i >= args.length || kind != Opt) 0 else args(i) match { case arg if longOptTokens(arg) > 0 => longOptTokens(arg) case arg if shortOptTokens(arg) > 0 => shortOptTokens(arg) case _ => 0 } private[scopt] def apply(i: Int, args: Seq[String]): Either[String, String] = if (i >= args.length || kind != Opt) Left("Option does not match") else args(i) match { case arg if longOptTokens(arg) == 2 || shortOptTokens(arg) == 2 => token(i + 1, args) map {Right(_)} getOrElse Left("Missing value after " + arg) case arg if longOptTokens(arg) == 1 && read.tokensToRead == 1 => Right(arg drop (fullName + ":").length) case arg if shortOptTokens(arg) == 1 && read.tokensToRead == 1 => Right(arg drop ("-" + shortOptOrBlank + ":").length) case _ => Right("") } private[scopt] def token(i: Int, args: Seq[String]): Option[String] = if (i >= args.length || kind != Opt) None else Some(args(i)) private[scopt] def usage: String = kind match { case Head | Note | Check => _desc case Cmd => "Command: " + _parser.commandExample(Some(this)) + NL + _desc case Arg => WW + name + NLTB + _desc case Opt if read.arity == 2 => WW + (_shortOpt map { o => "-" + o + ":" + keyValueString + " | " } getOrElse { "" }) + fullName + ":" + keyValueString + NLTB + _desc case Opt if read.arity == 1 => WW + (_shortOpt map { o => "-" + o + " " + valueString + " | " } getOrElse { "" }) + fullName + " " + valueString + NLTB + _desc case Opt => WW + (_shortOpt map { o => "-" + o + " | " } getOrElse { "" }) + fullName + NLTB + _desc } private[scopt] def usageTwoColumn(col1Length: Int): String = { def spaceToDesc(str: String) = if (str.length <= col1Length) str + " " * (col1Length - str.length) else str.dropRight(WW.length) + NL + " " * col1Length kind match { case Head | Note | Check => _desc case Cmd => usageColumn1 + _desc case Arg => spaceToDesc(usageColumn1 + WW) + _desc case Opt if read.arity == 2 => spaceToDesc(usageColumn1 + WW) + _desc case Opt if read.arity == 1 => spaceToDesc(usageColumn1 + WW) + _desc case Opt => spaceToDesc(usageColumn1 + WW) + _desc } } private[scopt] def usageColumn1: String = kind match { case Head | Note | Check => "" case Cmd => "Command: " + _parser.commandExample(Some(this)) + NL case Arg => WW + name case Opt if read.arity == 2 => WW + (_shortOpt map { o => "-" + o + ", " } getOrElse { "" }) + fullName + ":" + keyValueString case Opt if read.arity == 1 => WW + (_shortOpt map { o => "-" + o + ", " } getOrElse { "" }) + fullName + " " + valueString case Opt => WW + (_shortOpt map { o => "-" + o + ", " } getOrElse { "" }) + fullName } private[scopt] def keyValueString: String = (_keyName getOrElse defaultKeyName) + "=" + valueString private[scopt] def valueString: String = (_valueName getOrElse defaultValueName) def shortDescription: String = kind match { case Opt => "option " + fullName case Cmd => "command " + fullName case _ => "argument " + fullName } def fullName: String = kind match { case Opt => "--" + name case _ => name } private[scopt] def argName: String = kind match { case Arg if getMinOccurs == 0 => "[" + fullName + "]" case _ => fullName } } private[scopt] object OptionDef { val UNBOUNDED = Int.MaxValue val NL = System.getProperty("line.separator") val WW = " " val TB = " " val NLTB = NL + TB val NLNL = NL + NL val column1MaxLength = 25 + WW.length val defaultKeyName = "" val defaultValueName = "" val atomic = new java.util.concurrent.atomic.AtomicInteger def generateId: Int = atomic.incrementAndGet def makeSuccess[A]: Either[A, Unit] = Right(()) } scopt-3.5.0/src/test/000077500000000000000000000000001272715210000144125ustar00rootroot00000000000000scopt-3.5.0/src/test/scala/000077500000000000000000000000001272715210000154755ustar00rootroot00000000000000scopt-3.5.0/src/test/scala/scopt/000077500000000000000000000000001272715210000166255ustar00rootroot00000000000000scopt-3.5.0/src/test/scala/scopt/ImmutableParserSpec.scala000066400000000000000000000743011272715210000235460ustar00rootroot00000000000000import java.security.{AccessControlException, Permission} import java.util.concurrent.atomic.{AtomicBoolean, AtomicReference} import org.specs2._ import java.util.{Calendar, GregorianCalendar} import java.io.{ByteArrayOutputStream, File} import java.net.{ URI, InetAddress } import scala.concurrent.duration.Duration import scala.util.Try class ImmutableParserSpec extends Specification { def is = args(sequential = true) ^ s2""" This is a specification to check the immutable parser opt[Unit]('f', "foo") action { x => x } should parse () out of --foo ${unitParser("--foo")} parse () out of -f ${unitParser("-f")} opt[Unit]('a', "alice"); opt[Unit]('b', "bob"); opt[Unit]("alicebob") abbr("ab") action { x => x } should parse () out of -ab ${groupParser("-ab")} parse () out of -abab ${groupParser("-abab")} opt[Int]('f', "foo") action { x => x } should parse 1 out of --foo 1 ${intParser("--foo", "1")} parse 1 out of --foo:1 ${intParser("--foo:1")} parse 1 out of --foo=1 ${intParser("--foo=1")} parse 1 out of -f 1 ${intParser("-f", "1")} parse 1 out of -f:1 ${intParser("-f:1")} parse 1 out of -f=1 ${intParser("-f=1")} fail to parse --foo ${intParserFail{"--foo"}} fail to parse --foo bar ${intParserFail("--foo", "bar")} fail to parse --foo=bar ${intParserFail("--foo=bar")} opt[String]("foo") action { x => x } should parse "bar" out of --foo bar ${stringParser("--foo", "bar")} parse "bar" out of --foo:bar ${stringParser("--foo:bar")} parse "bar" out of --foo=bar ${stringParser("--foo=bar")} opt[Double]("foo") action { x => x } should parse 1.0 out of --foo 1.0 ${doubleParser("--foo", "1.0")} parse 1.0 out of --foo:1.0 ${doubleParser("--foo:1.0")} parse 1.0 out of --foo=1.0 ${doubleParser("--foo=1.0")} fail to parse --foo bar ${doubleParserFail("--foo", "bar")} fail to parse --foo=bar ${doubleParserFail("--foo=bar")} opt[Boolean]("foo") action { x => x } should parse true out of --foo true ${trueParser("--foo", "true")} parse true out of --foo:true ${trueParser("--foo:true")} parse true out of --foo=true ${trueParser("--foo=true")} parse true out of --foo 1 ${trueParser("--foo", "1")} parse true out of --foo:1 ${trueParser("--foo:1")} fail to parse --foo bar ${boolParserFail("--foo", "bar")} fail to parse --foo=bar ${boolParserFail("--foo=bar")} opt[BigDecimal]("foo") action { x => x } should parse 1.0 out of --foo 1.0 ${bigDecimalParser("--foo", "1.0")} parse 1.0 out of --foo=1.0 ${bigDecimalParser("--foo=1.0")} fail to parse --foo bar ${bigDecimalParserFail("--foo", "bar")} fail to parse --foo=bar ${bigDecimalParserFail("--foo=bar")} opt[Calendar]("foo") action { x => x } should parse 2000-01-01 out of --foo 2000-01-01 ${calendarParser("--foo", "2000-01-01")} parse 2000-01-01 out of --foo=2000-01-01 ${calendarParser("--foo=2000-01-01")} fail to parse --foo bar ${calendarParserFail("--foo", "bar")} fail to parse --foo=bar ${calendarParserFail("--foo=bar")} opt[File]("foo") action { x => x } should parse test.txt out of --foo test.txt ${fileParser("--foo", "test.txt")} parse test.txt out of --foo=test.txt ${fileParser("--foo=test.txt")} opt[URI]("foo") action { x => x } should parse http://github.com/ out of --foo http://github.com/ ${uriParser("--foo", "http://github.com/")} parse http://github.com/ out of --foo=http://github.com/ ${uriParser("--foo=http://github.com/")} opt[InetAddress]("foo") action { x => x } should parse 8.8.8.8 out of --foo 8.8.8.8 ${inetAddressParser("--foo", "8.8.8.8")} parse 8.8.8.8 out of --foo=8.8.8.8 ${inetAddressParser("--foo=8.8.8.8")} opt[Duration]("foo") action { x => x } should parse 30s out of --foo 30s ${durationParser("--foo", "30s")} parse 30s out of --foo=30s ${durationParser("--foo=30s")} opt[(String, Int)]("foo") action { x => x } should parse ("k", 1) out of --foo k=1 ${pairParser("--foo", "k=1")} parse ("k", 1) out of --foo:k=1 ${pairParser("--foo:k=1")} parse ("k", 1) out of --foo=k=1 ${pairParser("--foo=k=1")} fail to parse --foo ${pairParserFail("--foo")} fail to parse --foo bar ${pairParserFail("--foo", "bar")} fail to parse --foo k=bar ${pairParserFail("--foo", "k=bar")} fail to parse --foo=k=bar ${pairParserFail("--foo=k=bar")} opt[Seq[Int]]("foo") action { x => x } should parse Seq(1,2,3) out of --foo "1,2,3" ${seqParser("--foo","1,2,3")} parse Seq(1,2,3) out of "--foo=1,2,3" ${seqParser("--foo=1,2,3")} fail to parse --foo ${seqParserFail("--foo")} opt[Map[String,Boolean]]("foo") action { x => x } should parse Map("true" -> true, "false" -> false) out of --foo "true=true,false=false" ${mapParser("--foo","true=true,false=false")} parse Map("true" -> true, "false" -> false) out of "--foo=true=true,false=false" ${mapParser("--foo=true=true,false=false")} fail to parse --foo ${mapParserFail("foo")} opt[Seq[(String,String)]]("foo") action { x => x } should parse Map("key" -> "1", "key" -> "2") out of --foo "key=1,false=false" ${seqTupleParser("--foo","key=1,key=2")} fail to parse --foo ${seqTupleParserFail("foo")} opt[String]("foo") required() action { x => x } should fail to parse Nil ${requiredFail()} opt[Unit]("debug") hidden() action { x => x } should parse () out of --debug ${unitParserHidden("--debug")} unknown options should fail to parse by default ${intParserFail("-z", "bar")} opt[(String, Int)]("foo") action { x => x } validate { x => if (x > 0) success else failure("Option --foo must be >0") } should fail to parse --foo 0 ${validFail("--foo", "0")} opt[Unit]('f', "foo") action { x => x }; checkConfig { c => if (c.flag) success else failure("flag is false") } should parse () out of --foo ${checkSuccess("--foo")} fail to parse empty ${checkFail()} arg[Int]("") action { x => x } should parse 80 out of 80 ${intArg("80")} be required and should fail to parse Nil ${intArgFail()} arg[String](""); arg[String]("") action { x => x } should parse "b" out of a b ${multipleArgs("a", "b")} arg[String]("") action { x => x} unbounded() optional(); arg[String]("") optional() should parse "b" out of a b ${unboundedArgs("a", "b")} parse nothing out of Nil ${emptyArgs()} cmd("update") action { x => x } children( opt[Unit]("foo") action { x => x} ) should parse () out of update ${cmdParser("update")} parse () out of update --foo ${cmdParser("update", "--foo")} fail to parse --foo ${cmdParserFail("--foo")} arg[String]("") action { x => x}; cmd("update") children(arg[String](""), arg[String]("")) ; cmd("commit") should parse commit out of update foo bar commit ${cmdPosParser("update", "foo", "bar", "commit")} parse commit out of commit commit ${cmdPosParser("commit", "commit")} fail to parse foo update ${cmdPosParserFail("foo", "update")} cmd("backend") children( cmd("update") children(arg[String]("") action { x => x} )) should parse foo out of backend update foo ${nestedCmdParser("backend", "update", "foo")} fail to paser backend foo ${nestedCmdParserFail("backend", "foo")} help("help") if OneColumn should print usage text --help ${helpParserOneColumn()} help("help") if TwoColumns should print usage text --help ${helpParserTwoColumns()} reportError("foo") should print "Error: foo\n" ${reportErrorParser("foo")} reportWarning("foo") should print "Warning: foo\n" ${reportWarningParser("foo")} showHeader should print "scopt 3.x\n" ${showHeaderParser()} showUsage should print usage text ${showUsageParser()} terminationSafeParser should not terminate on `--help` ${terminationSafeParser("--help")} not terminate on `--version` ${terminationSafeParser("--version")} """ import SpecUtil._ val unitParser1 = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") opt[Unit]('f', "foo").action( (x, c) => c.copy(flag = true) ) opt[Unit]("debug").action( (x, c) => c.copy(debug = true) ) help("help") } def unitParser(args: String*) = { val result = unitParser1.parse(args.toSeq, Config()) result.get.flag === true } def unitParserHidden(args: String*) = { val result = unitParser1.parse(args.toSeq, Config()) result.get.debug === true } val groupParser1 = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") opt[Unit]('a', "alice") opt[Unit]('b', "bob") opt[Unit]("alicebob").abbr("ab").action( (x, c) => c.copy(flag = true) ) help("help") } def groupParser(args: String*) = { val result = groupParser1.parse(args.toSeq, Config()) result.get.flag === true } val intParser1 = new scopt.OptionParser[Config]("scopt") { override def showUsageOnError = true head("scopt", "3.x") opt[Int]('f', "foo").action( (x, c) => c.copy(intValue = x) ) help("help") } def intParser(args: String*) = { val result = intParser1.parse(args.toSeq, Config()) result.get.intValue === 1 } def intParserFail(args: String*) = { val result = intParser1.parse(args.toSeq, Config()) result === None } val stringParser1 = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") opt[String]("foo").action( (x, c) => c.copy(stringValue = x) ) help("help") } def stringParser(args: String*) = { val result = stringParser1.parse(args.toSeq, Config()) result.get.stringValue === "bar" } val doubleParser1 = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") opt[Double]("foo").action( (x, c) => c.copy(doubleValue = x) ) help("help") } def doubleParser(args: String*) = { val result = doubleParser1.parse(args.toSeq, Config()) result.get.doubleValue === 1.0 } def doubleParserFail(args: String*) = { val result = doubleParser1.parse(args.toSeq, Config()) result === None } val boolParser1 = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") opt[Boolean]("foo").action( (x, c) => c.copy(boolValue = x) ) help("help") } def trueParser(args: String*) = { val result = boolParser1.parse(args.toSeq, Config()) result.get.boolValue === true } def boolParserFail(args: String*) = { val result = boolParser1.parse(args.toSeq, Config()) result === None } val bigDecimalParser1 = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") opt[BigDecimal]("foo").action( (x, c) => c.copy(bigDecimalValue = x) ) help("help") } def bigDecimalParser(args: String*) = { val result = bigDecimalParser1.parse(args.toSeq, Config()) result.get.bigDecimalValue === BigDecimal("1.0") } def bigDecimalParserFail(args: String*) = { val result = bigDecimalParser1.parse(args.toSeq, Config()) result === None } val calendarParser1 = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") opt[Calendar]("foo").action( (x, c) => c.copy(calendarValue = x) ) help("help") } def calendarParser(args: String*) = { val result = calendarParser1.parse(args.toSeq, Config()) result.get.calendarValue.getTime === new GregorianCalendar(2000, Calendar.JANUARY, 1).getTime } def calendarParserFail(args: String*) = { val result = calendarParser1.parse(args.toSeq, Config()) result === None } val fileParser1 = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") opt[File]("foo").action( (x, c) => c.copy(fileValue = x) ) help("help") } def fileParser(args: String*) = { val result = fileParser1.parse(args.toSeq, Config()) result.get.fileValue === new File("test.txt") } val uriParser1 = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") opt[URI]("foo").action( (x, c) => c.copy(uriValue = x) ) help("help") } def uriParser(args: String*) = { val result = uriParser1.parse(args.toSeq, Config()) result.get.uriValue === new URI("http://github.com/") } val inetAddressParser1 = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") opt[InetAddress]("foo").action( (x, c) => c.copy(inetAddressValue = x) ) help("help") } def inetAddressParser(args: String*) = { val result = inetAddressParser1.parse(args.toSeq, Config()) result.get.inetAddressValue === InetAddress.getByName("8.8.8.8") } val durationParser1 = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") opt[Duration]("foo").action( (x, c) => c.copy(durationValue = x) ) help("help") } def durationParser(args: String*) = { val result = durationParser1.parse(args.toSeq, Config()) result.get.durationValue.toMillis === 30000L } val pairParser1 = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") opt[(String, Int)]("foo").action({ case ((k, v), c) => c.copy(key = k, intValue = v) }) help("help") } def pairParser(args: String*) = { val result = pairParser1.parse(args.toSeq, Config()) (result.get.key === "k") and (result.get.intValue === 1) } def pairParserFail(args: String*) = { val result = pairParser1.parse(args.toSeq, Config()) result === None } val seqParser1 = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") opt[Seq[Int]]("foo").action({ case (s, c) => c.copy(seqInts = s) }) help("help") } def seqParser(args: String*) = { val result = seqParser1.parse(args.toSeq, Config()) result.get.seqInts === Seq(1,2,3) } def seqParserFail(args: String*) = { val result = seqParser1.parse(args.toSeq, Config()) result === None } val mapParser1 = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") opt[Map[String,Boolean]]("foo").action({ case (s, c) => c.copy(mapStringToBool = s) }) help("help") } def mapParser(args: String*) = { val result = mapParser1.parse(args.toSeq, Config()) result.get.mapStringToBool === Map("true" -> true,"false" -> false) } def mapParserFail(args: String*) = { val result = mapParser1.parse(args.toSeq, Config()) result === None } val seqTupleParser1 = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") opt[Seq[(String,String)]]("foo").action({ case (s, c) => c.copy(seqTupleStringString = s) }) help("help") } def seqTupleParser(args: String*) = { val result = seqTupleParser1.parse(args.toSeq, Config()) result.get.seqTupleStringString === List("key" -> "1","key" -> "2") } def seqTupleParserFail(args: String*) = { val result = seqTupleParser1.parse(args.toSeq, Config()) result === None } //parse Map("true" -> true, "false" -> false) out of --foo "true=true,false=false" ${mapParser("--foo","true=true,false=false")} val requireParser1 = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") opt[String]("foo").required().action( (x, c) => c.copy(stringValue = x) ) help("help") } def requiredFail(args: String*) = { val result = requireParser1.parse(args.toSeq, Config()) result === None } val validParser1 = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") opt[Int]('f', "foo").action( (x, c) => c.copy(intValue = x) ). validate( x => if (x > 0) success else failure("Option --foo must be >0") ). validate( x => failure("Just because") ) help("help") } def validFail(args: String*) = { val result = validParser1.parse(args.toSeq, Config()) result === None } val checkParser1 = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") opt[Unit]('f', "foo").action( (x, c) => c.copy(flag = true) ) checkConfig { c => if (c.flag) success else failure("flag is false") } help("help") } def checkSuccess(args: String*) = { val result = checkParser1.parse(args.toSeq, Config()) result.get.flag === true } def checkFail(args: String*) = { val result = checkParser1.parse(args.toSeq, Config()) result === None } val intArgParser1 = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") arg[Int]("").action( (x, c) => c.copy(intValue = x) ) help("help") } def intArg(args: String*) = { val result = intArgParser1.parse(args.toSeq, Config()) result.get.intValue === 80 } def intArgFail(args: String*) = { val result = intArgParser1.parse(args.toSeq, Config()) result === None } val multipleArgsParser1 = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") arg[String]("").action( (x, c) => c.copy(a = x) ) arg[String]("").action( (x, c) => c.copy(b = x) ) help("help") } def multipleArgs(args: String*) = { val result = multipleArgsParser1.parse(args.toSeq, Config()) (result.get.a === "a") and (result.get.b === "b") } val unboundedArgsParser1 = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") arg[String]("").action( (x, c) => c.copy(a = x) ).unbounded().optional() arg[String]("").action( (x, c) => c.copy(b = x) ).optional() help("help") } def unboundedArgs(args: String*) = { val result = unboundedArgsParser1.parse(args.toSeq, Config()) (result.get.a === "b") and (result.get.b === "") } def emptyArgs(args: String*) = { val result = unboundedArgsParser1.parse(args.toSeq, Config()) (result.get.a === "") and (result.get.b === "") } val cmdParser1 = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") cmd("update").action( (x, c) => c.copy(flag = true) ).children( opt[Unit]("foo").action( (x, c) => c.copy(stringValue = "foo") ) ) help("help") } def cmdParser(args: String*) = { val result = cmdParser1.parse(args.toSeq, Config()) result.get.flag === true } def cmdParserFail(args: String*) = { val result = cmdParser1.parse(args.toSeq, Config()) result === None } val cmdPosParser1 = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") arg[String]("").action( (x, c) => c.copy(a = x) ) cmd("update").action( (x, c) => c.copy(flag = true) ).children( arg[String]("").action( (x, c) => c.copy(b = x) ), arg[String]("") ) cmd("commit") help("help") } def cmdPosParser(args: String*) = { val result = cmdPosParser1.parse(args.toSeq, Config()) result.get.a === "commit" } def cmdPosParserFail(args: String*) = { val result = cmdPosParser1.parse(args.toSeq, Config()) result === None } val nestedCmdParser1 = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") cmd("backend").text("commands to manipulate backends:\n"). action( (x, c) => c.copy(flag = true) ). children( cmd("update").children( arg[String]("").action( (x, c) => c.copy(a = x) ), checkConfig( c => if (c.a == "foo") success else failure("not foo") ) ) ) help("help") } def nestedCmdParser(args: String*) = { val result = nestedCmdParser1.parse(args.toSeq, Config()) result.get.a === "foo" } def nestedCmdParserFail(args: String*) = { val result = nestedCmdParser1.parse(args.toSeq, Config()) result === None } def helpParserOneColumn(args: String*) = { case class Config(foo: Int = -1, out: File = new File("."), xyz: Boolean = false, libName: String = "", maxCount: Int = -1, verbose: Boolean = false, debug: Boolean = false, mode: String = "", files: Seq[File] = Seq(), keepalive: Boolean = false, jars: Seq[File] = Seq(), kwargs: Map[String,String] = Map()) val parser = new scopt.OptionParser[Config]("scopt") { override def renderingMode = scopt.RenderingMode.OneColumn head("scopt", "3.x") opt[Int]('f', "foo").action( (x, c) => c.copy(foo = x) ).text("foo is an integer property") opt[File]('o', "out").required().valueName(""). action( (x, c) => c.copy(out = x) ). text("out is a required file property") opt[(String, Int)]("max").action({ case ((k, v), c) => c.copy(libName = k, maxCount = v) }). validate( x => if (x._2 > 0) success else failure("Value must be >0") ). keyValueName("", ""). text("maximum count for ") opt[Seq[File]]('j', "jars").valueName(",...").action( (x,c) => c.copy(jars = x) ).text("jars to include") opt[Map[String,String]]("kwargs").valueName("k1=v1,k2=v2...").action( (x, c) => c.copy(kwargs = x) ).text("other arguments") opt[Unit]("verbose").action( (_, c) => c.copy(verbose = true) ).text("verbose is a flag") opt[Unit]("debug").hidden().action( (_, c) => c.copy(debug = true) ).text("this option is hidden in the usage text") help("help").text("prints this usage text") arg[File]("...").unbounded().optional().action( (x, c) => c.copy(files = c.files :+ x) ).text("optional unbounded args") note("some notes.".newline) cmd("update").action( (_, c) => c.copy(mode = "update") ). text("update is a command."). children( opt[Unit]("not-keepalive").abbr("nk").action( (_, c) => c.copy(keepalive = false) ).text("disable keepalive"), opt[Boolean]("xyz").action( (x, c) => c.copy(xyz = x) ).text("xyz is a boolean property"), opt[Unit]("debug-update").hidden().action( (_, c) => c.copy(debug = true) ).text("this option is hidden in the usage text"), checkConfig( c => if (c.keepalive && c.xyz) failure("xyz cannot keep alive") else success ) ) } parser.parse(args.toSeq, Config()) val expectedUsage = """scopt 3.x Usage: scopt [update] [options] [...] -f | --foo foo is an integer property -o | --out out is a required file property --max:= maximum count for -j ,... | --jars ,... jars to include --kwargs k1=v1,k2=v2... other arguments --verbose verbose is a flag --help prints this usage text ... optional unbounded args some notes. Command: update [options] update is a command. -nk | --not-keepalive disable keepalive --xyz xyz is a boolean property""".newlines val expectedHeader = """scopt 3.x""" (parser.header === expectedHeader) and (parser.usage === expectedUsage) } def helpParserTwoColumns(args: String*) = { case class Config(foo: Int = -1, out: File = new File("."), xyz: Boolean = false, libName: String = "", maxCount: Int = -1, verbose: Boolean = false, debug: Boolean = false, mode: String = "", files: Seq[File] = Seq(), keepalive: Boolean = false, jars: Seq[File] = Seq(), kwargs: Map[String,String] = Map()) val parser = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") opt[Int]('f', "foo").action( (x, c) => c.copy(foo = x) ).text("foo is an integer property") opt[File]('o', "out").required().valueName(""). action( (x, c) => c.copy(out = x) ). text("out is a required file property") opt[(String, Int)]("max").action({ case ((k, v), c) => c.copy(libName = k, maxCount = v) }). validate( x => if (x._2 > 0) success else failure("Value must be >0") ). keyValueName("", ""). text("maximum count for ") opt[Seq[File]]('j', "jars").valueName(",...").action( (x,c) => c.copy(jars = x) ).text("jars to include") opt[Map[String,String]]("kwargs").valueName("k1=v1,k2=v2...").action( (x, c) => c.copy(kwargs = x) ).text("other arguments") opt[Unit]("verbose").action( (_, c) => c.copy(verbose = true) ).text("verbose is a flag") opt[Unit]("debug").hidden().action( (_, c) => c.copy(debug = true) ).text("this option is hidden in the usage text") help("help").text("prints this usage text") arg[File]("...").unbounded().optional().action( (x, c) => c.copy(files = c.files :+ x) ).text("optional unbounded args") note("some notes.".newline) cmd("update").action( (_, c) => c.copy(mode = "update") ). text("update is a command."). children( opt[Unit]("not-keepalive").abbr("nk").action( (_, c) => c.copy(keepalive = false) ).text("disable keepalive"), opt[Boolean]("xyz").action( (x, c) => c.copy(xyz = x) ).text("xyz is a boolean property"), opt[Unit]("debug-update").hidden().action( (_, c) => c.copy(debug = true) ).text("this option is hidden in the usage text"), checkConfig( c => if (c.keepalive && c.xyz) failure("xyz cannot keep alive") else success ) ) } parser.parse(args.toSeq, Config()) val expectedUsage= """scopt 3.x Usage: scopt [update] [options] [...] -f, --foo foo is an integer property -o, --out out is a required file property --max:= maximum count for -j, --jars ,... jars to include --kwargs k1=v1,k2=v2... other arguments --verbose verbose is a flag --help prints this usage text ... optional unbounded args some notes. Command: update [options] update is a command. -nk, --not-keepalive disable keepalive --xyz xyz is a boolean property""".newlines val expectedHeader = """scopt 3.x""" (parser.header === expectedHeader) and (parser.usage === expectedUsage) } val printParser1 = new scopt.OptionParser[Config]("scopt") { head("scopt", "3.x") help("help") text("prints this usage text") } lazy val terminationSafeParser1 = new scopt.OptionParser[Config]("scopt") { override def terminate(exitState: Either[String, Unit]): Unit = () version("version") opt[Unit]("debug") action { (x, c) => c.copy(debug = true) } help("help") text("prints this usage text") } def terminationSafeParser(args: String*) = { val result = terminationSafeParser1.parse(args.toSeq, Config()) result.isDefined } def printParserError(body: scopt.OptionParser[Config] => Unit): String = { val errStream = new ByteArrayOutputStream() Console.withErr(errStream) { body(printParser1) } errStream.toString("UTF-8") } def printParserOut(body: scopt.OptionParser[Config] => Unit): String = { val outStream = new ByteArrayOutputStream() Console.withOut(outStream) { body(printParser1) } outStream.toString("UTF-8") } def reportErrorParser(msg: String) = { printParserError(_.reportError(msg)) === "Error: foo".newline } def reportWarningParser(msg: String) = { printParserError(_.reportWarning(msg)) === "Warning: foo".newline } def showHeaderParser() = { printParserOut(_.showHeader()) === "scopt 3.x".newline } def showUsageParser() = { printParserOut(_.showUsage()) === """scopt 3.x Usage: scopt [options] --help prints this usage text """ } case class Config(flag: Boolean = false, intValue: Int = 0, stringValue: String = "", doubleValue: Double = 0.0, boolValue: Boolean = false, debug: Boolean = false, bigDecimalValue: BigDecimal = BigDecimal("0.0"), calendarValue: Calendar = new GregorianCalendar(1900, Calendar.JANUARY, 1), fileValue: File = new File("."), uriValue: URI = new URI("http://localhost"), inetAddressValue: InetAddress = InetAddress.getByName("0.0.0.0"), durationValue: Duration = Duration("0s"), key: String = "", a: String = "", b: String = "", seqInts: Seq[Int] = Seq(), mapStringToBool: Map[String,Boolean] = Map(), seqTupleStringString: Seq[(String, String)] = Nil) } scopt-3.5.0/src/test/scala/scopt/MutableParserSpec.scala000066400000000000000000000425261272715210000232240ustar00rootroot00000000000000import org.specs2._ import java.io.{ByteArrayOutputStream, File} class MutableParserSpec extends Specification { def is = args(sequential = true) ^ s2""" This is a specification to check the mutable parser opt[Unit]('f', "foo") foreach { x => x } should parse () out of --foo ${unitParser("--foo")} parse () out of -f ${unitParser("-f")} opt[Int]('f', "foo") foreach { x => x } should parse 1 out of --foo 1 ${intParser("--foo", "1")} parse 1 out of --foo:1 ${intParser("--foo:1")} parse 1 out of --foo=1 ${intParser("--foo=1")} parse 1 out of -f 1 ${intParser("-f", "1")} parse 1 out of -f:1 ${intParser("-f:1")} fail to parse --foo ${intParserFail{"--foo"}} fail to parse --foo bar ${intParserFail("--foo", "bar")} fail to parse --foo=bar ${intParserFail("--foo=bar")} opt[String]("foo") foreach { x => x } should parse "bar" out of --foo bar ${stringParser("--foo", "bar")} parse "bar" out of --foo:bar ${stringParser("--foo:bar")} parse "bar" out of --foo=bar ${stringParser("--foo=bar")} opt[Double]("foo") foreach { x => x } should parse 1.0 out of --foo 1.0 ${doubleParser("--foo", "1.0")} parse 1.0 out of --foo:1.0 ${doubleParser("--foo:1.0")} parse 1.0 out of --foo=1.0 ${doubleParser("--foo=1.0")} fail to parse --foo bar ${doubleParserFail("--foo", "bar")} fail to parse --foo=bar ${doubleParserFail("--foo=bar")} opt[Boolean]("foo") foreach { x => x } should parse true out of --foo true ${trueParser("--foo", "true")} parse true out of --foo:true ${trueParser("--foo:true")} parse true out of --foo=true ${trueParser("--foo=true")} parse true out of --foo 1 ${trueParser("--foo", "1")} parse true out of --foo:1 ${trueParser("--foo:1")} parse true out of --foo=1 ${trueParser("--foo=1")} fail to parse --foo bar ${boolParserFail("--foo", "bar")} fail to parse --foo=bar ${boolParserFail("--foo=bar")} opt[(String, Int)]("foo") foreach { x => x } should parse ("k", 1) out of --foo k=1 ${pairParser("--foo", "k=1")} parse ("k", 1) out of --foo:k=1 ${pairParser("--foo:k=1")} parse ("k", 1) out of --foo=k=1 ${pairParser("--foo=k=1")} fail to parse --foo ${pairParserFail("--foo")} fail to parse --foo bar ${pairParserFail("--foo", "bar")} fail to parse --foo k=bar ${pairParserFail("--foo", "k=bar")} fail to parse --foo=k=bar ${pairParserFail("--foo=k=bar")} opt[String]("foo") required() foreach { x => x } should fail to parse Nil ${requiredFail()} opt[Unit]("debug") hidden() foreach { x => x } should parse () out of --debug ${unitParserHidden("--debug")} unknown options should fail to parse by default ${intParserFail("-z", "bar")} opt[(String, Int)]("foo") foreach { x => x } validate { x => if (x > 0) success else failure("Option --foo must be >0") } should fail to parse --foo 0 ${validFail("--foo", "0")} arg[Int]("") foreach { x => x } should parse 80 out of 80 ${intArg("80")} be required and should fail to parse Nil ${intArgFail()} arg[String](""); arg[String]("") foreach { x => x } should parse "b" out of a b ${multipleArgs("a", "b")} arg[String]("") foreach { x => x} unbounded() optional(); arg[String]("") optional() should parse "b" out of a b ${unboundedArgs("a", "b")} parse nothing out of Nil ${emptyArgs()} cmd("update") foreach { x => x } should parse () out of update ${cmdParser("update")} help("help") if OneColumn should print usage text --help ${helpParserOneColumn()} help("help") if TwoColumn should print usage text --help ${helpParserTwoColumns()} reportError("foo") should print "Error: foo\n" ${reportErrorParser("foo")} reportWarning("foo") should print "Warning: foo\n" ${reportWarningParser("foo")} showHeader should print "scopt 3.x\n" ${showHeaderParser()} showUsage should print usage text ${showUsageParser()} """ import SpecUtil._ def unitParser(args: String*) = { var foo = false val parser = new scopt.OptionParser[Unit]("scopt") { head("scopt", "3.x") opt[Unit]('f', "foo").foreach( _ => foo = true ) help("help") } val result = parser.parse(args.toSeq) (result === true) and (foo === true) } def unitParserHidden(args: String*) = { var debug = false val parser = new scopt.OptionParser[Unit]("scopt") { head("scopt", "3.x") opt[Unit]("debug").hidden().foreach( _ => debug = true ) help("help") } val result = parser.parse(args.toSeq) debug === true } def intParser(args: String*) = { var foo = 0 val parser = new scopt.OptionParser[Unit]("scopt") { head("scopt", "3.x") opt[Int]('f', "foo").foreach( x => foo = x ) help("help") } parser.parse(args.toSeq) foo === 1 } def intParserFail(args: String*) = { var foo = 0 val parser = new scopt.OptionParser[Unit]("scopt") { head("scopt", "3.x") opt[Int]('f', "foo").foreach( x => foo = x ) help("help") } parser.parse(args.toSeq) === false } def stringParser(args: String*) = { var foo = "" val parser = new scopt.OptionParser[Unit]("scopt") { head("scopt", "3.x") opt[String]("foo").foreach( x => foo = x ) help("help") } parser.parse(args.toSeq) foo === "bar" } def doubleParser(args: String*) = { var foo = 0.0 val parser = new scopt.OptionParser[Unit]("scopt") { head("scopt", "3.x") opt[Double]("foo").foreach( x => foo = x ) help("help") } parser.parse(args.toSeq) foo === 1.0 } def doubleParserFail(args: String*) = { var foo = 0.0 val parser = new scopt.OptionParser[Unit]("scopt") { head("scopt", "3.x") opt[Double]("foo").foreach( x => foo = x ) help("help") } parser.parse(args.toSeq) === false } def trueParser(args: String*) = { var foo = false val parser = new scopt.OptionParser[Unit]("scopt") { head("scopt", "3.x") opt[Boolean]("foo").foreach( x => foo = x ) help("help") } parser.parse(args.toSeq) foo === true } def boolParserFail(args: String*) = { var foo = false val parser = new scopt.OptionParser[Unit]("scopt") { head("scopt", "3.x") opt[Boolean]("foo").foreach( x => foo = x ) help("help") } parser.parse(args.toSeq) === false } def pairParser(args: String*) = { var foo = "" var value = 0 val parser = new scopt.OptionParser[Unit]("scopt") { head("scopt", "3.x") opt[(String, Int)]("foo").foreach({ case (k, v) => foo = k value = v }) help("help") } parser.parse(args.toSeq) (foo === "k") and (value === 1) } def pairParserFail(args: String*) = { var foo = "" var value = 0 val parser = new scopt.OptionParser[Unit]("scopt") { head("scopt", "3.x") opt[(String, Int)]("foo").foreach({ case (k, v) => foo = k value = v }) help("help") } parser.parse(args.toSeq) === false } def requiredFail(args: String*) = { var foo = "" val parser = new scopt.OptionParser[Unit]("scopt") { head("scopt", "3.x") opt[String]("foo").required().foreach( x => foo = x ) help("help") } parser.parse(args.toSeq) === false } def validFail(args: String*) = { var foo = 0 val parser = new scopt.OptionParser[Unit]("scopt") { head("scopt", "3.x") opt[Int]('f', "foo").foreach( x => foo = x ). validate( x => if (x > 0) success else failure("Option --foo must be >0") ). validate( x => failure("Just because") ) help("help") } parser.parse(args.toSeq) === false } def intArg(args: String*) = { var port = 0 val parser = new scopt.OptionParser[Unit]("scopt") { head("scopt", "3.x") arg[Int]("").foreach( x => port = x ) help("help") } parser.parse(args.toSeq) port === 80 } def intArgFail(args: String*) = { var port = 0 val parser = new scopt.OptionParser[Unit]("scopt") { head("scopt", "3.x") arg[Int]("").foreach( x => port = x ) help("help") } parser.parse(args.toSeq) === false } def multipleArgs(args: String*) = { var a = "" var b = "" val parser = new scopt.OptionParser[Unit]("scopt") { head("scopt", "3.x") arg[String]("").foreach( x => a = x ) arg[String]("").foreach( x => b = x ) help("help") } parser.parse(args.toSeq) (a === "a") and (b === "b") } def unboundedArgs(args: String*) = { var a = "" var b = "" val parser = new scopt.OptionParser[Unit]("scopt") { head("scopt", "3.x") arg[String]("").foreach( x => a = x ).unbounded() arg[String]("").foreach( x => b = x ) help("help") } parser.parse(args.toSeq) (a === "b") and (b === "") } def emptyArgs(args: String*) = { var a = "" var b = "" val parser = new scopt.OptionParser[Unit]("scopt") { head("scopt", "3.x") arg[String]("").foreach( x => a = x ).unbounded().optional() arg[String]("").foreach( x => b = x ).optional() help("help") } parser.parse(args.toSeq) === true } def cmdParser(args: String*) = { var foo = false val parser = new scopt.OptionParser[Unit]("scopt") { head("scopt", "3.x") cmd("update").foreach( _ => foo = true ) help("help") } val result = parser.parse(args.toSeq) (result === true) and (foo === true) } def helpParserOneColumn(args: String*) = { case class Config(foo: Int = -1, out: File = new File("."), xyz: Boolean = false, libName: String = "", maxCount: Int = -1, verbose: Boolean = false, debug: Boolean = false, mode: String = "", files: Seq[File] = Seq(), keepalive: Boolean = false) var c = Config() val parser = new scopt.OptionParser[Unit]("scopt") { override def renderingMode = scopt.RenderingMode.OneColumn head("scopt", "3.x") opt[Int]('f', "foo").foreach( x => c = c.copy(foo = x) ). text("foo is an integer property") opt[File]('o', "out").required().valueName(""). foreach( x => c = c.copy(out = x) ).text("out is a required file property") opt[(String, Int)]("max").foreach( { case (k, v) => c = c.copy(libName = k, maxCount = v) }). validate( x => if (x._2 > 0) success else failure("Value must be >0") ). keyValueName("", ""). text("maximum count for ") opt[Unit]("verbose").foreach( _ => c = c.copy(verbose = true) ). text("verbose is a flag") opt[Unit]("debug").hidden().foreach( _ => c = c.copy(debug = true) ). text("this option is hidden in the usage text") help("help").text("prints this usage text") arg[File]("...").unbounded().optional(). foreach( x => c = c.copy(files = c.files :+ x) ). text("optional unbounded args") note("some notes.".newline) cmd("update").foreach( _ => c.copy(mode = "update") ). text("update is a command."). children( opt[Unit]("not-keepalive").abbr("nk"). foreach( _ => c.copy(keepalive = false) ).text("disable keepalive"), opt[Boolean]("xyz").foreach( x => c = c.copy(xyz = x) ).text("xyz is a boolean property"), opt[Unit]("debug-update").hidden(). foreach( _ => c = c.copy(debug = true) ). text("this option is hidden in the usage text") ) } parser.parse(args.toSeq) parser.usage === """scopt 3.x Usage: scopt [update] [options] [...] -f | --foo foo is an integer property -o | --out out is a required file property --max:= maximum count for --verbose verbose is a flag --help prints this usage text ... optional unbounded args some notes. Command: update [options] update is a command. -nk | --not-keepalive disable keepalive --xyz xyz is a boolean property""" } def helpParserTwoColumns(args: String*) = { case class Config(foo: Int = -1, out: File = new File("."), xyz: Boolean = false, libName: String = "", maxCount: Int = -1, verbose: Boolean = false, debug: Boolean = false, mode: String = "", files: Seq[File] = Seq(), keepalive: Boolean = false) var c = Config() val parser = new scopt.OptionParser[Unit]("scopt") { head("scopt", "3.x") opt[Int]('f', "foo").foreach( x => c = c.copy(foo = x) ). text("foo is an integer property") opt[File]('o', "out").required().valueName(""). foreach( x => c = c.copy(out = x) ).text("out is a required file property") opt[(String, Int)]("max").foreach( { case (k, v) => c = c.copy(libName = k, maxCount = v) }). validate( x => if (x._2 > 0) success else failure("Value must be >0") ). keyValueName("", ""). text("maximum count for ") opt[Unit]("verbose").foreach( _ => c = c.copy(verbose = true) ). text("verbose is a flag") opt[Unit]("debug").hidden().foreach( _ => c = c.copy(debug = true) ). text("this option is hidden in the usage text") help("help").text("prints this usage text") arg[File]("...").unbounded().optional(). foreach( x => c = c.copy(files = c.files :+ x) ). text("optional unbounded args") note("some notes.".newline) cmd("update").foreach( _ => c.copy(mode = "update") ). text("update is a command."). children( opt[Unit]("not-keepalive").abbr("nk"). foreach( _ => c.copy(keepalive = false) ).text("disable keepalive"), opt[Boolean]("xyz").foreach( x => c = c.copy(xyz = x) ).text("xyz is a boolean property"), opt[Unit]("debug-update").hidden(). foreach( _ => c = c.copy(debug = true) ). text("this option is hidden in the usage text") ) } parser.parse(args.toSeq) parser.usage === """scopt 3.x Usage: scopt [update] [options] [...] -f, --foo foo is an integer property -o, --out out is a required file property --max:= maximum count for --verbose verbose is a flag --help prints this usage text ... optional unbounded args some notes. Command: update [options] update is a command. -nk, --not-keepalive disable keepalive --xyz xyz is a boolean property""" } def printParserError(body: scopt.OptionParser[Unit] => Unit): String = { val parser = new scopt.OptionParser[Unit]("scopt") { head("scopt", "3.x") help("help") text("prints this usage text") } val bos = new ByteArrayOutputStream() Console.withErr(bos) { body(parser) } bos.toString("UTF-8") } def printParserOut(body: scopt.OptionParser[Unit] => Unit): String = { val parser = new scopt.OptionParser[Unit]("scopt") { head("scopt", "3.x") help("help") text("prints this usage text") } val bos = new ByteArrayOutputStream() Console.withOut(bos) { body(parser) } bos.toString("UTF-8") } def reportErrorParser(msg: String) = { printParserError(_.reportError(msg)) === "Error: foo".newline } def reportWarningParser(msg: String) = { printParserError(_.reportWarning(msg)) === "Warning: foo".newline } def showHeaderParser() = { printParserOut(_.showHeader) === "scopt 3.x".newline } def showUsageParser() = { printParserOut(_.showUsage) === """scopt 3.x Usage: scopt [options] --help prints this usage text """ } } scopt-3.5.0/src/test/scala/scopt/SpecUtil.scala000066400000000000000000000003571272715210000213670ustar00rootroot00000000000000 object SpecUtil { val envLineSeparator = util.Properties.lineSeparator implicit class RichString(self: String) { def newline: String = self + envLineSeparator def newlines: String = self.lines.mkString(envLineSeparator) } }