refactor:: simplify/improve CLI API

This commit is contained in:
tydeu 2021-12-23 23:43:01 -05:00
parent c9128d1ce6
commit 2b0989ea28
3 changed files with 255 additions and 246 deletions

View file

@ -11,7 +11,7 @@ import Lake.Config.Resolve
import Lake.Config.Util
import Lake.Util.Error
import Lake.Util.MainM
import Lake.Util.CliT
import Lake.Util.Cli
import Lake.CLI.Init
import Lake.CLI.Help
import Lake.CLI.Build
@ -23,7 +23,7 @@ namespace Lake
-- # CLI
structure CliState where
structure LakeOptions where
rootDir : FilePath := "."
configFile : FilePath := defaultConfigFile
leanInstall? : Option LeanInstall := none
@ -31,65 +31,65 @@ structure CliState where
subArgs : List String := []
wantsHelp : Bool := false
abbrev CliM := CliT <| StateT CliState MainM
abbrev CliStateM := StateT LakeOptions <| MainM
abbrev CliM := ArgsT CliStateM
namespace Cli
open CliT
-- ## Basic Actions
/-- Print out a line wih the given message and then exit with an error code. -/
protected def error (msg : String) (rc : UInt32 := 1) : CliM α := do
protected def error (msg : String) (rc : UInt32 := 1) : MainM α := do
IO.eprintln s!"error: {msg}" |>.catchExceptions fun _ => ()
exit rc
instance : MonadError CliM := ⟨Cli.error⟩
instance : MonadLift IO CliM := ⟨MonadError.runIO⟩
instance : MonadError MainM := ⟨Cli.error⟩
instance : MonadLift IO MainM := ⟨MonadError.runIO⟩
-- ## State Management
-- ## Basic State Management
def getRootDir : CliM FilePath :=
(·.rootDir) <$> getThe CliState
def getRootDir : CliStateM FilePath :=
(·.rootDir) <$> get
def setRootDir (dir : FilePath) : CliM PUnit :=
modifyThe CliState fun st => {st with rootDir := dir}
def setRootDir (dir : FilePath) : CliStateM PUnit :=
modify fun st => {st with rootDir := dir}
def getConfigFile : CliM FilePath :=
(·.configFile) <$> getThe CliState
def getConfigFile : CliStateM FilePath :=
(·.configFile) <$> get
def setConfigFile (file : FilePath) : CliM PUnit :=
modifyThe CliState fun st => {st with configFile := file}
def setConfigFile (file : FilePath) : CliStateM PUnit :=
modify ({· with configFile := file})
def getSubArgs : CliM (List String) :=
(·.subArgs) <$> getThe CliState
def getSubArgs : CliStateM (List String) :=
(·.subArgs) <$> get
def setSubArgs (args : List String) : CliM PUnit :=
modifyThe CliState fun st => {st with subArgs := args}
def setSubArgs (args : List String) : CliStateM PUnit :=
modify fun st => {st with subArgs := args}
def getWantsHelp : CliM Bool :=
(·.wantsHelp) <$> getThe CliState
def getWantsHelp : CliStateM Bool :=
(·.wantsHelp) <$> get
def setWantsHelp : CliM PUnit :=
modifyThe CliState fun st => {st with wantsHelp := true}
def setWantsHelp : CliStateM PUnit :=
modify fun st => {st with wantsHelp := true}
def setLean (lean : String) : CliM PUnit := do
def setLean (lean : String) : CliStateM PUnit := do
let leanInstall? ← findLeanCmdInstall? lean
modifyThe CliState fun st => {st with leanInstall?}
modify fun st => {st with leanInstall?}
def getLeanInstall? : CliM (Option LeanInstall) :=
(·.leanInstall?) <$> getThe CliState
def getLeanInstall? : CliStateM (Option LeanInstall) :=
(·.leanInstall?) <$> get
def getLakeInstall? : CliM (Option LakeInstall) :=
(·.lakeInstall?) <$> getThe CliState
def getLakeInstall? : CliStateM (Option LakeInstall) :=
(·.lakeInstall?) <$> get
-- ## Complex Actions
-- ## Complex State Management
def loadPkg (args : List String := []) : CliM Package := do
def loadPkg (args : List String := []) : CliStateM Package := do
let dir ← getRootDir; let file ← getConfigFile
setupLeanSearchPath (← getLeanInstall?) (← getLakeInstall?)
Package.load dir args (dir / file)
def loadConfig (args : List String := []) : CliM (Workspace × Package) := do
def loadConfig (args : List String := []) : CliStateM (Workspace × Package) := do
let pkg ← loadPkg args
let ws ← Workspace.ofPackage pkg
let packageMap ← resolveDeps ws pkg |>.run LogMethods.eio (m := IO)
@ -97,31 +97,31 @@ def loadConfig (args : List String := []) : CliM (Workspace × Package) := do
({ws with packageMap}, pkg)
/-- Get the Lean installation. Error if missing. -/
def getLeanInstall : CliM LeanInstall := do
def getLeanInstall : CliStateM LeanInstall := do
if let some leanInstall ← getLeanInstall? then
return leanInstall
else
error "could not detect a Lean installation"
/-- Get the Lake installation. Error if missing. -/
def getLakeInstall : CliM LakeInstall := do
def getLakeInstall : CliStateM LakeInstall := do
if let some lakeInstall ← getLakeInstall? then
return lakeInstall
else
error "could not detect the configuration of the Lake installation"
/-- Get the Lean and Lake installation. Error if either is missing. -/
def getInstall : CliM (LeanInstall × LakeInstall) := do
def getInstall : CliStateM (LeanInstall × LakeInstall) := do
return (← getLeanInstall, ← getLakeInstall)
/-- Perform the given build action using information from CLI. -/
def runBuildM (ws : Workspace) (x : BuildM α) : CliM α := do
def runBuildM (ws : Workspace) (x : BuildM α) : CliStateM α := do
let (leanInstall, lakeInstall) ← getInstall
let ctx ← mkBuildContext ws leanInstall lakeInstall
x.run LogMethods.io ctx
/-- Variant of `runBuildM` that discards the build monad's output. -/
def runBuildM_ (ws : Workspace) (x : BuildM α) : CliM PUnit :=
def runBuildM_ (ws : Workspace) (x : BuildM α) : CliStateM PUnit :=
discard <| runBuildM ws x
-- ## Argument Parsing
@ -135,7 +135,7 @@ def takeArg (errMsg : String := "missing argument") : CliM String := do
Verify that there are no CLI arguments remaining
before running the given action.
-/
def noArgsRem (act : CliM α) : CliM α := do
def noArgsRem (act : CliStateM α) : CliM α := do
let args ← getArgs
if args.isEmpty then act else
error s!"unexpected arguments: {" ".intercalate args}"
@ -162,29 +162,25 @@ def longOption : (opt : String) → CliM PUnit
| "--" => do setSubArgs <| ← takeArgs
| opt => unknownLongOption opt
/-- Splits a long option of the form `--long=arg` into `--long arg`. -/
def longOptionOrEq (optStr : String) : CliM PUnit :=
let eqPos := optStr.posOf '='
let arg := optStr.drop eqPos.succ
let opt := optStr.take eqPos
if arg.isEmpty then
longOption opt
else do
consArg arg
longOption opt
def lakeOption :=
option {
short := shortOption
long := longOption
longShort := shortOptionWithArg shortOption
}
-- ## Commands
def withPackage [MonadLiftT m CliM] (x : Package → LakeT m α) : CliM α := do
def withPackage [MonadLiftT m CliStateM] (x : Package → LakeT m α) : CliStateM α := do
let (ws, pkg) ← loadConfig
let (lean, lake) ← getInstall
liftM <| x pkg |>.run {lean, lake, opaqueWs := ws}
def withContext [MonadLiftT m CliM] (x : LakeT m α) : CliM α :=
def withContext [MonadLiftT m CliStateM] (x : LakeT m α) : CliStateM α :=
withPackage fun _ => x
/-- Run the given script from the given package with the given arguments. -/
def script (pkg : Package) (name : String) (args : List String) : CliM PUnit := do
def script (pkg : Package) (name : String) (args : List String) : CliStateM PUnit := do
if let some script := pkg.scripts.find? name then
if (← getWantsHelp) then
if let some help := script.help? then
@ -194,19 +190,19 @@ def script (pkg : Package) (name : String) (args : List String) : CliM PUnit :=
else
exit <| ← withContext <| script.run args
else
pkg.scripts.forM (m := CliM) fun name _ => do
pkg.scripts.forM (m := CliStateM) fun name _ => do
IO.println <| name.toString (escape := false)
error s!"unknown script '{name}'"
/-- Verify the Lean version Lake was built with matches that of the Lean installation. -/
def verifyLeanVersion : CliM PUnit := do
def verifyLeanVersion : CliStateM PUnit := do
let lean ← getLeanInstall
unless lean.githash == Lean.githash do
let githash := if lean.githash.isEmpty then "nothing" else lean.githash
error s!"expected Lean commit {Lean.githash}, but got {lean.githash}"
/-- Output the detected installs and verify the Lean version. -/
def verifyInstall : CliM PUnit := do
def verifyInstall : CliStateM PUnit := do
IO.println s!"Lean:\n{repr <| ← getLeanInstall?}"
IO.println s!"Lake:\n{repr <| ← getLakeInstall?}"
verifyLeanVersion
@ -221,7 +217,7 @@ If no configuration file exists, exit silently with `noConfigFileCode` (i.e, 2).
The `print-paths` command is used internally by Lean 4 server.
-/
def printPaths (imports : List String := []) : CliM PUnit := do
def printPaths (imports : List String := []) : CliStateM PUnit := do
let (lean, lake) ← getInstall
let configFile := (← getRootDir) / (← getConfigFile)
if (← configFile.pathExists) then
@ -239,27 +235,54 @@ def serve (pkg : Package) (args : Array String := #[]) : LakeT IO UInt32 := do
env (← getLean).toString <| #["--server"] ++ pkg.moreServerArgs ++ args
def command : (cmd : String) → CliM PUnit
| "new" => do processOptions; noArgsRem <| new (← takeArg "missing package name")
| "init" => do processOptions; noArgsRem <| init (← takeArg "missing package name")
| "run" => do processOptions; noArgsRem <| script (← loadPkg) (← takeArg "missing script") (← getSubArgs)
| "env" => do exit <| ← withContext <| env (← takeArg "missing command") (← takeArgs).toArray
| "serve" => do processOptions; let args ← getSubArgs; exit <| ← noArgsRem <| withPackage fun pkg => serve pkg args.toArray
| "configure" => do processOptions; let (ws, pkg) ← loadConfig (← getSubArgs); noArgsRem <| runBuildM ws pkg.buildDepOleans
| "print-paths" => do processOptions; printPaths (← takeArgs)
| "build" => do processOptions; let (ws, pkg) ← loadConfig (← getSubArgs); runBuildM ws <| build pkg (← takeArgs)
| "clean" => do processOptions; noArgsRem <| (← loadPkg (← getSubArgs)).clean
| "self-check" => do processOptions; noArgsRem <| verifyInstall
| "help" => do IO.println <| help (← takeArg?)
| cmd => error s!"unknown command '{cmd}'"
| "new" => do
processOptions lakeOption
let pkgName ← takeArg "missing package name"
noArgsRem <| new pkgName
| "init" => do
processOptions lakeOption
let pkgName ← takeArg "missing package name"
noArgsRem <| init pkgName
| "run" => do
processOptions lakeOption
let scriptName ← takeArg "missing script name"
noArgsRem <| script (← loadPkg) scriptName (← getSubArgs)
| "env" => do
let cmd ← takeArg "missing command"; let args ← takeArgs
exit <| ← withContext <| env cmd args.toArray
| "serve" => do
let args ← getSubArgs
noArgsRem <| exit <| ← withPackage fun pkg => serve pkg args.toArray
| "configure" => do
processOptions lakeOption
let (ws, pkg) ← loadConfig (← getSubArgs)
noArgsRem <| runBuildM ws pkg.buildDepOleans
| "print-paths" => do
processOptions lakeOption
printPaths (← takeArgs)
| "build" => do
processOptions lakeOption
let (ws, pkg) ← loadConfig (← getSubArgs)
runBuildM ws <| build pkg (← takeArgs)
| "clean" => do
processOptions lakeOption
noArgsRem <| (← loadPkg (← getSubArgs)).clean
| "self-check" => do
processOptions lakeOption
noArgsRem <| verifyInstall
| "help" => do
IO.println <| help (← takeArg?)
| cmd =>
error s!"unknown command '{cmd}'"
def processArgs : CliM PUnit := do
match (← getArgs) with
| [] => IO.println usage
| ["--version"] => IO.println uiVersionString
| _ => -- normal CLI
processLeadingOptions -- between `lake` and command
processLeadingOptions lakeOption -- between `lake` and command
if let some cmd ← takeArg? then
processLeadingOptions -- between command and args
processLeadingOptions lakeOption -- between command and args
if (← getWantsHelp) then
IO.println <| help cmd
else
@ -272,13 +295,7 @@ end Cli
open Cli in
def CliM.run (self : CliM α) (args : List String) : IO UInt32 := do
let (leanInstall?, lakeInstall?) ← findInstall?
let initSt := {leanInstall?, lakeInstall?}
let methods := {
shortOption,
longOption := longOptionOrEq,
longShortOption := unknownLongOption,
}
match (← CliT.run self args methods |>.run' initSt |>.toIO') with
match (← self args |>.run' {leanInstall?, lakeInstall?} |>.toIO') with
| Except.ok _ => pure 0
| Except.error rc => pure rc

161
Lake/Util/Cli.lean Normal file
View file

@ -0,0 +1,161 @@
/-
Copyright (c) 2021 Mac Malone. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mac Malone
-/
namespace Lake
/-!
Defines the abstract CLI interface for Lake.
-/
-- # Types
def ArgList := List String
@[inline] def ArgList.mk (args : List String) : ArgList :=
args
abbrev ArgsT := StateT ArgList
@[inline] def ArgsT.run (args : List String) (self : ArgsT m α) : m (α × List String) :=
StateT.run self args
@[inline] def ArgsT.run' [Functor m] (args : List String) (self : ArgsT m α) : m α :=
StateT.run' self args
structure OptionHandlers (m : Type u → Type v) (α : Type u) where
/-- Process a long option (ex. `--long` or `"--long foo bar"`). -/
long : String → m α
/-- Process a short option (ex. `-x` or `--`). -/
short : Char → m α
/-- Process a long short option (ex. `-long`, `-xarg`, `-xyz`). -/
longShort : String → m α
-- # Utilities
variable [Monad m] [MonadStateOf ArgList m]
/-- Get the remaining argument list. -/
@[inline] def getArgs : m (List String) :=
get
/-- Replace the argument list. -/
@[inline] def setArgs (args : List String) : m PUnit :=
set (ArgList.mk args)
/-- Take the head of the remaining argument list (or none if empty). -/
@[inline] def takeArg? : m (Option String) :=
modifyGet fun | [] => (none, []) | arg :: args => (some arg, args)
/-- Take the remaining argument list, leaving only an empty list. -/
@[inline] def takeArgs : m (List String) :=
modifyGet fun args => (args, [])
/-- Add a string to the head of the remaining argument list. -/
@[inline] def consArg (arg : String) : m PUnit :=
modify fun args => arg :: args
/-- Process a short option of the form `-x=arg`. -/
@[inline] def shortOptionWithEq (handle : Char → m α) (opt : String) : m α := do
consArg (opt.drop 3); handle opt[1]
/-- Process a short option of the form `"-x arg"`. -/
@[inline] def shortOptionWithSpace (handle : Char → m α) (opt : String) : m α := do
consArg <| opt.drop 2 |>.trimLeft; handle opt[1]
/-- Process a short option of the form `-xarg`. -/
@[inline] def shortOptionWithArg (handle : Char → m α) (opt : String) : m α := do
consArg (opt.drop 2); handle opt[1]
/-- Process a multiple short options grouped together (ex. `-xyz` as `x`, `y`, `z`). -/
@[inline] def multiShortOption (handle : Char → m PUnit) (opt : String) : m PUnit := do
for i in [1:opt.length] do handle opt[i]
/-- Splits a long option of the form `"--long foo bar"` into `--long` and `"foo bar"`. -/
@[inline] def longOptionOrSpace (handle : String → m α) (opt : String) : m α :=
let pos := opt.posOf ' '
let arg := opt.drop pos.succ
if arg.isEmpty then
handle opt
else do
consArg arg
handle <| opt.take pos |>.trimLeft
/-- Splits a long option of the form `--long=arg` into `--long` and `arg`. -/
@[inline] def longOptionOrEq (handle : String → m α) (opt : String) : m α :=
let pos := opt.posOf '='
let arg := opt.drop pos.succ
if arg.isEmpty then
handle opt
else do
consArg arg
handle <| opt.take pos
/-- Process a long option of the form `--long`, `--long=arg`, `"--long arg"`. -/
@[inline] def longOption (handle : String → m α) : String → m α :=
longOptionOrEq <| longOptionOrSpace handle
/-- Process a short option of the form `-x`, `-x=arg`, `-x arg`, or `-long`. -/
@[inline] def shortOption
(shortHandle : Char → m α) (longHandle : String → m α)
(opt : String) : m α :=
if opt.length == 2 then -- `-x`
shortHandle opt[1]
else -- `-c(.+)`
match opt[2] with
| '=' => -- `-x=arg`
shortOptionWithEq shortHandle opt
| ' ' => -- `"-x arg"`
shortOptionWithSpace shortHandle opt
| _ => -- `-long`
longHandle opt
/--
Process an option, short or long, using the given handlers.
An option is an argument of length > 1 starting with a dash (`-`).
An option may consume additional elements of the argument list.
-/
@[inline] def option (handlers : OptionHandlers m α) (opt : String) : m α :=
if opt[1] == '-' then -- `--(.*)`
longOption handlers.long opt
else
shortOption handlers.short handlers.longShort opt
/-- Process the head argument of the list using `handle` if it is an option. -/
def processLeadingOption (handle : String → m PUnit) : m PUnit := do
match (← getArgs) with
| [] => pure ()
| arg :: args =>
if arg.length > 1 && arg[0] == '-' then -- `-(.+)`
setArgs args
handle arg
/--
Process the leading options of the remaining argument list.
Consumes empty leading arguments in the argument list.
-/
partial def processLeadingOptions (handle : String → m PUnit) : m PUnit := do
match (← getArgs) with
| [] => pure ()
| arg :: args =>
let len := arg.length
if len > 1 && arg[0] == '-' then -- `-(.+)`
setArgs args
handle arg
processLeadingOptions handle
else if len == 0 then -- skip empty leading args
setArgs args
processLeadingOptions handle
/-- Process every option and collect the remaining arguments into an `Array`. -/
partial def collectArgs (option : String → m PUnit) (args : Array String := #[]) : m (Array String) := do
processLeadingOptions option
match (← takeArg?) with
| some arg => collectArgs option (args.push arg)
| none => args
/-- Process every option in the argument list. -/
@[inline] def processOptions (handle : String → m PUnit) : m PUnit := do
setArgs (← collectArgs handle).toList

View file

@ -1,169 +0,0 @@
/-
Copyright (c) 2021 Mac Malone. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mac Malone
-/
namespace Lake
--------------------------------------------------------------------------------
-- # CLI Transformer Definition
--------------------------------------------------------------------------------
-- Involves trickery patterned after `MacroM`
-- to allow `CliMethods` to refer to `CliT`
constant CliMethodsRefPointed (m : Type → Type v) : PointedType.{0}
def CliMethodsRef (m : Type → Type v) : Type := (CliMethodsRefPointed m).type
abbrev CliT (m : Type → Type v) :=
ReaderT (CliMethodsRef m) <| StateT (List String) m
instance [Pure m] [Inhabited α] : Inhabited (CliT m α) :=
⟨fun _ _ => pure (Inhabited.default, Inhabited.default)⟩
structure CliMethods (m : Type → Type v) where
longOption : String → CliT m PUnit
longShortOption : String → CliT m PUnit
shortOption : Char → CliT m PUnit
instance [Pure m] : Inhabited (CliMethods m) :=
⟨Inhabited.default, Inhabited.default, Inhabited.default⟩
namespace CliMethodsRef
unsafe def mkImp (methods : CliMethods m) : CliMethodsRef m :=
unsafeCast methods
@[implementedBy mkImp]
constant mk (methods : CliMethods m) : CliMethodsRef m :=
(CliMethodsRefPointed m).val
instance : Coe (CliMethods m) (CliMethodsRef m) := ⟨mk⟩
instance [Pure m] : Inhabited (CliMethodsRef m) := ⟨mk Inhabited.default⟩
unsafe def getImp [Pure m] (self : CliMethodsRef m) : CliMethods m :=
unsafeCast self
@[implementedBy getImp]
constant get [Pure m] (self : CliMethodsRef m) : CliMethods m
end CliMethodsRef
--------------------------------------------------------------------------------
-- # CLI Utilities
--------------------------------------------------------------------------------
namespace CliT
variable [Monad m]
/-- Run the CLI on the given argument list using the given methods. -/
def run (self : CliT m α) (args : List String) (methods : CliMethods m) : m α :=
ReaderT.run self methods |>.run' args
/-- Get the remaining argument list. -/
def getArgs : CliT m (List String) :=
get
/-- Replace the argument list. -/
def setArgs (args : List String) : CliT m PUnit :=
set args
/-- Take the head of the remaining argument list (or none if empty). -/
def takeArg? : CliT m (Option String) :=
modifyGet fun | [] => (none, []) | arg :: args => (some arg, args)
/-- Take the remaining argument list, leaving only an empty list. -/
def takeArgs : CliT m (List String) :=
modifyGet fun args => (args, [])
/-- Add a string to the head of the remaining argument list. -/
def consArg (arg : String) : CliT m PUnit :=
modify fun args => arg :: args
/-- Get the methods of this CLI. -/
def getMethods : CliT m (CliMethods m) :=
(·.get) <$> read
/-- Change the methods of this CLI. -/
def adaptMethods (f : CliMethods m → CliMethods m) (self : CliT m α) : CliT m α :=
self.adapt fun ref => CliMethodsRef.mk <| f ref.get
/-- Process a short option (ex. `-x` or `--`). -/
def shortOption (opt : Char) : CliT m PUnit :=
getMethods >>= (·.shortOption opt)
/-- Process a short option of the form `-x=arg`. -/
def shortOptionEq (opt : String) : CliT m PUnit := do
consArg (opt.drop 3); shortOption opt[1]
/-- Process a short option of the form `-xarg`. -/
def shortOptionArg (opt : String) : CliT m PUnit := do
consArg (opt.drop 2); shortOption opt[1]
/-- Process a short option of the form `"-x arg"`. -/
def shortOptionSpace (opt : String) : CliT m PUnit := do
consArg <| opt.drop 2 |>.trimLeft; shortOption opt[1]
/-- Process a multiple short options grouped together (ex. `-xyz` as `x`, `y`, `z`). -/
def multiShortOption (opt : String) : CliT m PUnit := do
for i in [1:opt.length] do shortOption opt[i]
/-- Process a long option (ex. `--long` or `"--long a1 a2"`). -/
def longOption (opt : String) : CliT m PUnit := do
getMethods >>= (·.longOption opt)
/-- Process a long short option (ex. `-long`, `-xarg`, `-xyz`). -/
def longShortOption (opt : String) : CliT m PUnit := do
getMethods >>= (·.longShortOption opt)
/--
Process an option, short or long.
An option is an argument of length > 1 starting with a dash (`-`).
An option may consume additional elements of the argument list.
-/
def option (opt : String) : CliT m PUnit :=
if opt[1] == '-' then -- `--(.*)`
longOption opt
else
if opt.length == 2 then -- `-x`
shortOption opt[1]
else -- `-c(.+)`
match opt[2] with
| '=' => -- `-x=arg`
shortOptionEq opt
| ' ' => -- `"-x arg"`
shortOptionSpace opt
| _ => -- `-long`
longShortOption opt
/-- Process the leading options of the remaining argument list. -/
partial def processLeadingOptions : CliT m PUnit := do
match (← getArgs) with
| [] => pure ()
| arg :: args =>
let len := arg.length
if len > 1 && arg[0] == '-' then -- `-(.+)`
setArgs args
option arg
processLeadingOptions
else if len == 0 then -- skip empty leading args
setArgs args
processLeadingOptions
/-- Process every option and collect the remaining arguments into the given `Array`. -/
partial def collectArgsInto (arr : Array String) : CliT m (Array String) := do
processLeadingOptions
match (← takeArg?) with
| some arg => collectArgsInto (arr.push arg)
| none => arr
/-- Process every option and collect the remaining arguments into an `Array`. -/
def collectArgs : CliT m (Array String) :=
collectArgsInto #[]
/-- Process every option in the argument list. -/
def processOptions : CliT m PUnit := do
setArgs (← collectArgs).toList
end CliT