Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Remove hard-coded support for mainargs.Leftover/Flag/SubParser to sup…
…port alternate implementations (#62) This PR moves the handling of `mainargs.Leftover`/`Flag`/`SubParser` from a hard-coded `ArgSig` that only works with `mainargs.Leftover` or `mainargs.Flag`, to properties of the `TokensReader` that can be configured to work with any custom type. Should probably be reviewed concurrently with com-lihaoyi/mill#1948, which is the motivation for this PR: we want to be able to define a CLI entrypoing taking `mill.define.Task[mainargs.Leftover[T]]` or equivalent, which is currently impossible due to the hard-coded nature of `mainargs.Leftover` (and `mainargs.Flag` etc.) # Major Changes 1. `ArgReader` is eliminated and `ArgSig` is greatly simplified to a single type with no subtypes or type parameters 2. `TokensReader` is split into 5primary sub-types - `.Simple`, `Constant`, `.Flag`, `.Leftover`, and `.Class`. These roughly mirror the original `{ArgSig,ArgReader}.{Simple,Flag,Leftover,Class}` case classes. The 5 sub-classes control behavior through `Renderer.scala`/`Invoker.scala`/`TokensGrouping.scala` in the same way. The major effect of moving the logic from `{ArgSig,ArgReader}` to `TokensReader` is that they now are no longer hard-coded to work with `mainargs.{Flag,Leftover,Subparser}` types. Now, anyone who has a custom type `Foo` can choose whether they want to define a `TokensReader.Simple` for it, or whether they want to define a `TokensReader.Leftover` or `TokensReader.Flag`. Similarly, people can define their own `TokensReader.Class` rather than relying on the default implementation in `mainargs.ParserForClass`. # Testing Tested with two new flavors of `VarargsTests` (now renamed `VarargsBasedTests`: 1. `VarargsWrappedTests` that exercises using a custom wrapper type to define a main entrypoints that takes `Wrapper[mainargs.Leftover[T]]`, 2. `VarargsCustomTests` that replaces `mainargs.Leftover[T]` entirely and defines main entrypoints that take `Wrapper[T]` 3. Added a `ConstantTests.scala` to exercise the code path, which was previously the `noTokens` codepath and un-tested in this repo 4. All existing tests pass # Notes 1. I chose to remove the type params from `ArgSig` because they weren't really paying for their complexity; most of the time we were passing around `ArgSig[_, _]`s anyway, so we weren't getting type safety, but they nevertheless caused tons of headaches trying to get types to line up. The un-typed ` default: Option[Any => Any], reader: TokensReader[_]` isn't great, but it's a lot easier to work with and TBH not really much less type-safe than the status quo 2. Because `ArgSig` and `TokensReader` now have a circular dependency on each other (via `TokensReader.Class` -> `MainData` -> `ArgSig` -> `TokensReader`), I moved them into the same file. This both makes the acyclic linter happy, and also kind of makes sense since they're now part of the same recursive data structure (v.s. previously `ArgSig` was the recursive data structure with `TokensReader`s hanging off of each node) 3. The new structure with `TokensReader` as a `sealed trait` with 5 distinct sub-types is different from what it was before, with `TokensReader` as a single `class` with a grab-bag of all possible fields and callbacks. I thought the `sealed trait` approach is much cleaner here, since they reflect exactly the data necessary 4 different scenarios we care about, whereas otherwise we'd find some fields meaningless in some cases e.g. `Flag` has no meaningful fields, `Leftover` doesn't care about `noTokens` or `alwaysRepeatable` or `allowEmpty`, etc.
- Loading branch information