The adopt-subcommands Reference Manual

Next: , Previous: , Up: (dir)   [Contents][Index]

The adopt-subcommands Reference Manual

This is the adopt-subcommands Reference Manual, version 0.2.2, generated automatically by Declt version 4.0 beta 2 "William Riker" on Thu Sep 15 03:11:46 2022 GMT+0.

Table of Contents


1 Introduction

#+TITLE: Adopt Subcommands
#+AUTHOR: Eric Timmons 

This library is an extension of Steve Losh's [[https://docs.stevelosh.com/adopt/][Adopt]] library (hands down my
favorite CLI options parser for CL) to support arbitrarily nested
subcommands. Not all features of Adopt are nicely supported yet (like producing
man pages for subcommands), but the basic functionality is there.

Subcommands are organized into folders and terminals. Both folders and
terminals can have options associated with them. Folders can additionally
contain other folders and terminals. Terminals *must* have a function
associated with them that will be called when a CLI command is dispatched. This
function must accept three arguments: a list of arguments (strings), an =EQL=
hash table of processed arguments, and a path object denoting all the folders
and terminal traversed.

Folders *may* have a function associated with them that is called before
attempting to further process the remaining arguments. This is useful to bind
dynamic variables or munge the options passed down to the later levels. This
function, if provided, must take four arguments: a list of arguments (strings),
an =EQL= hash table of options, a path object denoting all the folders and
traversed so far, and a thunk that must be called to continue processing.

This library tries to follow the same aesthetic as Adopt (such as interfaces
encouraged to be defined with =defparameter= and errors signaled with some
restarts provided) with two primary exceptions:

1. Any function you provide for terminals or folders is called with the results
   of processing the command line. You do not need to call
   =adopt:parse-options= or similar yourself.
2. Printing help and exiting is a common enough operation that this library
   provides native support for it, so you do not need to duplicate the checks
   for your program's help option everywhere.

* How to use

  In this section, we describe how to use the library, partially illustrated by
  examples from [[file:simple-demo.lisp][=simple-demo.lisp=]] (see the History section for more details on
  the provenance of this file).

  First, you must define your program's hierarchy. Terminals are defined using
  =make-subcommand-terminal=. It accepts the same arguments as
  =adopt:make-interface=, with the addition of the mandatory =:function= and
  optional =:key= keywords.

  The provided function will be called once we dispatch based on the command
  line arguments.

  The key is used if you want to specify the name of the subcommand; it is
  normally not required as it is derived from =:name=.

  For example:

  #+begin_src common-lisp
    (defparameter *ui/foo*
      (adopt-subcommands:make-subcommand-terminal
        :name "subex foo"
        :usage "foo [-a|-b]"
        :summary "foo some things"
        :help "foo some things"
        :contents (list *o/foo/a* *o/foo/b*)
        :function 'run/foo))
  #+end_src

  Folders are defined using =make-subcommand-folder=. It accepts the same
  arguments as =adopt:make-interface=, with the addition of the optional
  =:function= and =:key= keywords.

  If provided, the function will be called once we dispatch based on the
  command line arguments, before continuing to process the remainder of the
  arguments. It can be used to establish dynamic bindings or otherwise munge
  the arguments before continuing to process them.

  The key is used if you want to specify the name of the folder; it is normally
  not required as it is derived from =:name=.

  Additionally, the =:contents= of a folder can include other folders or
  terminals. For example:

  #+begin_src common-lisp
    (defparameter *ui*
      (adopt-subcommands:make-subcommand-folder
        :name "subex"
        :usage "[subcommand] [options]"
        :help "subcommand example program"
        :summary "an example program that uses subcommands"
        :contents (list *o/help* *o/version* *ui/foo* *ui/bar*)
        :function 'run/global))
  #+end_src

  Next, you must actually process the arguments and call the desired
  functions. This is accomplished using the =dispatch= function. It requires a
  folder as an argument. It can additionally be provided the list of
  arguments. Last, if =print-help-and-exit= is non-NIL, it should name our
  program's help option. If provided, this library processes as much of the
  command line as it can. Then, if the help option is non-NIL, prints the help
  and exits the program.

  For example:

  #+begin_src common-lisp
    (defun toplevel ()
      (handler-bind ((adopt-subcommands:folder-is-terminal 'adopt-subcommands:print-help-and-exit))
        (adopt-subcommands:dispatch *ui* :print-help-and-exit 'help)))
  #+end_src

* History

  This work was largely inspired by Steve Losh's [[https://old.reddit.com/r/Common_Lisp/comments/m7gjno/writing_small_cli_programs_in_common_lisp_steve/grdqq1j/][Reddit comment]] on a quick and
  dirty method to implement subcommands using Adopt. For a while, I had my own
  way of doing this (that also supported arbitrarily nested subcommands), but it
  was very nasty because I had somehow missed the existence of the
  =adopt:treat-as-argument= restart (d'oh). So after seeing that, I cleaned up my
  code and packaged it up as a library for others to use.

  For reference, the file =simple-demo.lisp= shows how to implement Steve's
  Reddit example using this library.

* Deficiencies

  These are the current deficiencies that I would like to fix some day:

  1. We reach into Adopt's internals a bit. Need to reach out to Steve and
     figure out if he's amenable to exporting the accessors we need and/or
     refactoring the help/man page generation to be more modular (and export
     the new components).

  2. Man page generation is not yet supported.

  3. Options need to come after the subcommand that defines them. For example,
     if =-a= is defined as part of =foo='s interface, the following will not
     work:

     #+begin_src shell
       my-program -a foo
     #+end_src

     But this will:

     #+begin_src shell
       my-program foo -a
     #+end_src

     This could be easily fixed for parameterless options. It could also be
     easily fixed for options with parameters, so long as the argument list has
     the parameter in the same token as the option (e.g. =--foo=5= instead of
     =--foo 5=). But a general purpose solution is harder and it is not obvious
     it's worth it.


2 Systems

The main system appears first, followed by any subsystem dependency.


Previous: , Up: Systems   [Contents][Index]

2.1 adopt-subcommands

Extend the Adopt command line processing library to handle nested subcommands.

Author

Eric Timmons <eric@timmons.dev>

Home Page

https://gitlab.com/daewok/adopt-subcommands

Bug Tracker

https://gitlab.com/daewok/adopt-subcommands/-/issues

License

MIT

Version

0.2.2

Dependencies
  • adopt (system).
  • bobbin (system).
  • split-sequence (system).
  • uiop (system).
Source

adopt-subcommands.asd.

Child Component

adopt-subcommands.lisp (file).


3 Files

Files are sorted by type and then listed depth-first from the systems components trees.


Previous: , Up: Files   [Contents][Index]

3.1 Lisp


3.1.1 adopt-subcommands/adopt-subcommands.asd

Source

adopt-subcommands.asd.

Parent Component

adopt-subcommands (system).

ASDF Systems

adopt-subcommands.


3.1.2 adopt-subcommands/adopt-subcommands.lisp

Source

adopt-subcommands.asd.

Parent Component

adopt-subcommands (system).

Packages

adopt-subcommands.

Public Interface
Internals

4 Packages

Packages are listed by definition order.


Previous: , Up: Packages   [Contents][Index]

4.1 adopt-subcommands

Source

adopt-subcommands.lisp.

Use List

common-lisp.

Public Interface
Internals

5 Definitions

Definitions are sorted by export status, category, package, and then by lexicographic order.


Next: , Previous: , Up: Definitions   [Contents][Index]

5.1 Public Interface


5.1.1 Ordinary functions

Function: add-subcommand (folder subcommand)

Add a subcommand to a folder.

Package

adopt-subcommands.

Source

adopt-subcommands.lisp.

Function: dispatch (interface &key arguments print-help-and-exit)

Given a subcommand UI (as returned by MAKE-SUBCOMMAND-FOLDER), dispatch based on the provided ARGUMENTS.

If PRINT-HELP-AND-EXIT is provided, it must be a symbol denoting the option corresponding to your UI’s help flag. If provided and the help option is present in the command line, the help will be printed and the program will exit when a terminal node is reached.

Package

adopt-subcommands.

Source

adopt-subcommands.lisp.

Function: make-subcommand-folder (&rest keys &key name summary usage help manual examples contents key function)

Create and return a subcommand folder.

If this is placed into another folder, that folder uses ‘key‘ to determine when to dispatch to this interface. If ‘key‘ is not specified, one is determined automatically by splitting ‘name‘ on space characters and taking the last entry.

When this subcommand is dispatched, ‘function‘ is called with a list of remaining arguments, an ‘eql‘ hash table containing all the parsed options, a path object representing all folders traversed (the path is currently only useful for passing to the PRINT-HELP), and a thunk that continues processing the subcommands when called. If not provided, the thunk is simply called.

The remaining arguments are passed directly to ‘adopt:make-interface‘.

Package

adopt-subcommands.

Source

adopt-subcommands.lisp.

Function: make-subcommand-terminal (&rest keys &key name summary usage help manual examples contents function key)

Create and return a terminal subcommand interface.

The subcommand folder into which this is placed uses ‘key‘ to determine when to dispatch to this interface. If ‘key‘ is not specified, one is determined automatically by splitting ‘name‘ on space characters and taking the last entry.

When this subcommand is dispatched, ‘function‘ is called with a list of remaining arguments, an ‘eql‘ hash table containing all the parsed options, and a path object representing all folders and terminal traversed (the path is currently only useful for passing to the PRINT-HELP). ‘function‘ is required.

The remaining arguments are passed directly to ‘adopt:make-interface‘.

Package

adopt-subcommands.

Source

adopt-subcommands.lisp.


5.1.2 Generic functions

Generic Reader: folder-is-terminal-folder (condition)
Package

adopt-subcommands.

Methods
Reader Method: folder-is-terminal-folder ((condition folder-is-terminal))
Source

adopt-subcommands.lisp.

Target Slot

folder.

Generic Reader: folder-is-terminal-path (condition)
Package

adopt-subcommands.

Methods
Reader Method: folder-is-terminal-path ((condition folder-is-terminal))
Source

adopt-subcommands.lisp.

Target Slot

path.

Generic Function: print-help (object &key stream program-name width option-width include-examples)

A GF version of ADOPT:PRINT-HELP that additionally dispatches for the objects defined by this library.

Package

adopt-subcommands.

Source

adopt-subcommands.lisp.

Methods
Method: print-help (ui &rest args)
Method: print-help ((ui subcommand-terminal) &rest args)
Method: print-help ((ui subcommand-folder) &rest args)
Method: print-help ((object subcommand-path) &rest args)
Generic Function: print-help-and-exit (object &key exit-code stream program-name width option-width include-examples &allow-other-keys)

A GF version of ADOPT:PRINT-HELP-AND-EXIT that additionally dispatches for the objects defined by this library.

Additionally, we use UIOP:QUIT since that works on more implementations than ADOPT:EXIT.

Package

adopt-subcommands.

Source

adopt-subcommands.lisp.

Methods
Method: print-help-and-exit ((c subcommand-error) &rest keys)
Method: print-help-and-exit (object &rest keys &key exit-code &allow-other-keys)
Generic Reader: unknown-subcommand-folder (condition)
Package

adopt-subcommands.

Methods
Reader Method: unknown-subcommand-folder ((condition unknown-subcommand))
Source

adopt-subcommands.lisp.

Target Slot

folder.

Generic Reader: unknown-subcommand-key (condition)
Package

adopt-subcommands.

Methods
Reader Method: unknown-subcommand-key ((condition unknown-subcommand))
Source

adopt-subcommands.lisp.

Target Slot

key.

Generic Reader: unknown-subcommand-path (condition)
Package

adopt-subcommands.

Methods
Reader Method: unknown-subcommand-path ((condition unknown-subcommand))
Source

adopt-subcommands.lisp.

Target Slot

path.


5.1.3 Conditions

Condition: folder-is-terminal

Signals that no terminal subcommand was found

Package

adopt-subcommands.

Source

adopt-subcommands.lisp.

Direct superclasses

subcommand-error.

Direct methods
Direct slots
Slot: folder
Initargs

:folder

Readers

folder-is-terminal-folder.

Writers

This slot is read-only.

Slot: path
Initargs

:path

Readers

folder-is-terminal-path.

Writers

This slot is read-only.

Condition: unknown-subcommand

Signals that a subcommand could not be found.

Package

adopt-subcommands.

Source

adopt-subcommands.lisp.

Direct superclasses

subcommand-error.

Direct methods
Direct slots
Slot: folder
Initargs

:folder

Readers

unknown-subcommand-folder.

Writers

This slot is read-only.

Slot: key
Initargs

:key

Readers

unknown-subcommand-key.

Writers

This slot is read-only.

Slot: path
Initargs

:path

Readers

unknown-subcommand-path.

Writers

This slot is read-only.


5.2 Internals


Next: , Previous: , Up: Internals   [Contents][Index]

5.2.1 Ordinary functions

Function: copy-hash-table (ht)

Copy a hash table

Package

adopt-subcommands.

Source

adopt-subcommands.lisp.

Function: extend-path (new-head path)
Package

adopt-subcommands.

Source

adopt-subcommands.lisp.

Function: hash-table-keys (ht)

Return a list of all keys in HT.

Package

adopt-subcommands.

Source

adopt-subcommands.lisp.

Function: key-from-name (name)

Generate a key from NAME by splitting on space and returning the last element.

Package

adopt-subcommands.

Source

adopt-subcommands.lisp.

Function: merge-hts (existing-ht new-ht)

Return a copy of EXISTING-HT with all key/value pairs from NEW-HT inserted into it.

Package

adopt-subcommands.

Source

adopt-subcommands.lisp.

Function: plist-remove-keys (plist &rest keys)

Return a new plist that contains all keys and values from PLIST, excepting any key in KEYS.

Package

adopt-subcommands.

Source

adopt-subcommands.lisp.

Function: print-entry-options (entry stream width option-width)

Print the options for an interface in a path.

This code is mostly copied from Adopt. See LICENSE for more information.

Package

adopt-subcommands.

Source

adopt-subcommands.lisp.

Function: print-path-help (path &key stream program-name width option-width include-examples)

Print the help for PATH. Prints the examples and usage information for the most specific interface on the path. Prints the options for all interfaces on the path.

This code is mostly copied from Adopt. See LICENSE for more information.

Package

adopt-subcommands.

Source

adopt-subcommands.lisp.

Function: signal-folder-is-terminal (folder path)
Package

adopt-subcommands.

Source

adopt-subcommands.lisp.

Function: signal-unknown-subcommand (folder path key)
Package

adopt-subcommands.

Source

adopt-subcommands.lisp.

Function: subcommand-folder-p (object)
Package

adopt-subcommands.

Source

adopt-subcommands.lisp.

Function: subcommand-terminal-p (object)
Package

adopt-subcommands.

Source

adopt-subcommands.lisp.


Next: , Previous: , Up: Internals   [Contents][Index]

5.2.2 Generic functions

Generic Reader: %function (object)
Package

adopt-subcommands.

Methods
Reader Method: %function ((subcommand subcommand))

automatically generated reader method

Source

adopt-subcommands.lisp.

Target Slot

function.

Generic Writer: (setf %function) (object)
Package

adopt-subcommands.

Methods
Writer Method: (setf %function) ((subcommand subcommand))

automatically generated writer method

Source

adopt-subcommands.lisp.

Target Slot

function.

Generic Function: dispatch-subcommand (interface &key arguments options print-help-and-exit path)
Package

adopt-subcommands.

Source

adopt-subcommands.lisp.

Methods
Method: dispatch-subcommand ((folder subcommand-folder) &key arguments options path print-help-and-exit)
Method: dispatch-subcommand ((terminal subcommand-terminal) &key arguments options path print-help-and-exit)
Generic Reader: interface (object)
Package

adopt-subcommands.

Methods
Reader Method: interface ((subcommand subcommand))

automatically generated reader method

Source

adopt-subcommands.lisp.

Target Slot

interface.

Generic Writer: (setf interface) (object)
Package

adopt-subcommands.

Methods
Writer Method: (setf interface) ((subcommand subcommand))

automatically generated writer method

Source

adopt-subcommands.lisp.

Target Slot

interface.

Generic Reader: key (object)
Package

adopt-subcommands.

Methods
Reader Method: key ((subcommand subcommand))

automatically generated reader method

Source

adopt-subcommands.lisp.

Target Slot

key.

Generic Writer: (setf key) (object)
Package

adopt-subcommands.

Methods
Writer Method: (setf key) ((subcommand subcommand))

automatically generated writer method

Source

adopt-subcommands.lisp.

Target Slot

key.

Generic Reader: path (object)
Package

adopt-subcommands.

Methods
Reader Method: path ((subcommand-path subcommand-path))

automatically generated reader method

Source

adopt-subcommands.lisp.

Target Slot

path.

Generic Writer: (setf path) (object)
Package

adopt-subcommands.

Methods
Writer Method: (setf path) ((subcommand-path subcommand-path))

automatically generated writer method

Source

adopt-subcommands.lisp.

Target Slot

path.

Generic Reader: subcommands (object)
Package

adopt-subcommands.

Methods
Reader Method: subcommands ((subcommand-folder subcommand-folder))

automatically generated reader method

Source

adopt-subcommands.lisp.

Target Slot

subcommands.

Generic Writer: (setf subcommands) (object)
Package

adopt-subcommands.

Methods
Writer Method: (setf subcommands) ((subcommand-folder subcommand-folder))

automatically generated writer method

Source

adopt-subcommands.lisp.

Target Slot

subcommands.


Next: , Previous: , Up: Internals   [Contents][Index]

5.2.3 Conditions

Condition: subcommand-error
Package

adopt-subcommands.

Source

adopt-subcommands.lisp.

Direct superclasses

error.

Direct subclasses
Direct methods

print-help-and-exit.


Previous: , Up: Internals   [Contents][Index]

5.2.4 Classes

Class: subcommand
Package

adopt-subcommands.

Source

adopt-subcommands.lisp.

Direct subclasses
Direct methods
Direct slots
Slot: key
Initargs

:key

Readers

key.

Writers

(setf key).

Slot: function
Package

common-lisp.

Initargs

:function

Readers

%function.

Writers

(setf %function).

Slot: interface
Initargs

:interface

Readers

interface.

Writers

(setf interface).

Class: subcommand-folder
Package

adopt-subcommands.

Source

adopt-subcommands.lisp.

Direct superclasses

subcommand.

Direct methods
Direct slots
Slot: subcommands
Initform

(make-hash-table :test (quote equal))

Readers

subcommands.

Writers

(setf subcommands).

Class: subcommand-path
Package

adopt-subcommands.

Source

adopt-subcommands.lisp.

Direct methods
Direct slots
Slot: path
Initargs

:path

Readers

path.

Writers

(setf path).

Class: subcommand-terminal
Package

adopt-subcommands.

Source

adopt-subcommands.lisp.

Direct superclasses

subcommand.

Direct methods

Appendix A Indexes


Next: , Previous: , Up: Indexes   [Contents][Index]

A.1 Concepts


Next: , Previous: , Up: Indexes   [Contents][Index]

A.2 Functions

Jump to:   %   (  
A   C   D   E   F   G   H   I   K   M   P   S   U  
Index Entry  Section

%
%function: Private generic functions
%function: Private generic functions

(
(setf %function): Private generic functions
(setf %function): Private generic functions
(setf interface): Private generic functions
(setf interface): Private generic functions
(setf key): Private generic functions
(setf key): Private generic functions
(setf path): Private generic functions
(setf path): Private generic functions
(setf subcommands): Private generic functions
(setf subcommands): Private generic functions

A
add-subcommand: Public ordinary functions

C
copy-hash-table: Private ordinary functions

D
dispatch: Public ordinary functions
dispatch-subcommand: Private generic functions
dispatch-subcommand: Private generic functions
dispatch-subcommand: Private generic functions

E
extend-path: Private ordinary functions

F
folder-is-terminal-folder: Public generic functions
folder-is-terminal-folder: Public generic functions
folder-is-terminal-path: Public generic functions
folder-is-terminal-path: Public generic functions
Function, add-subcommand: Public ordinary functions
Function, copy-hash-table: Private ordinary functions
Function, dispatch: Public ordinary functions
Function, extend-path: Private ordinary functions
Function, hash-table-keys: Private ordinary functions
Function, key-from-name: Private ordinary functions
Function, make-subcommand-folder: Public ordinary functions
Function, make-subcommand-terminal: Public ordinary functions
Function, merge-hts: Private ordinary functions
Function, plist-remove-keys: Private ordinary functions
Function, print-entry-options: Private ordinary functions
Function, print-path-help: Private ordinary functions
Function, signal-folder-is-terminal: Private ordinary functions
Function, signal-unknown-subcommand: Private ordinary functions
Function, subcommand-folder-p: Private ordinary functions
Function, subcommand-terminal-p: Private ordinary functions

G
Generic Function, %function: Private generic functions
Generic Function, (setf %function): Private generic functions
Generic Function, (setf interface): Private generic functions
Generic Function, (setf key): Private generic functions
Generic Function, (setf path): Private generic functions
Generic Function, (setf subcommands): Private generic functions
Generic Function, dispatch-subcommand: Private generic functions
Generic Function, folder-is-terminal-folder: Public generic functions
Generic Function, folder-is-terminal-path: Public generic functions
Generic Function, interface: Private generic functions
Generic Function, key: Private generic functions
Generic Function, path: Private generic functions
Generic Function, print-help: Public generic functions
Generic Function, print-help-and-exit: Public generic functions
Generic Function, subcommands: Private generic functions
Generic Function, unknown-subcommand-folder: Public generic functions
Generic Function, unknown-subcommand-key: Public generic functions
Generic Function, unknown-subcommand-path: Public generic functions

H
hash-table-keys: Private ordinary functions

I
interface: Private generic functions
interface: Private generic functions

K
key: Private generic functions
key: Private generic functions
key-from-name: Private ordinary functions

M
make-subcommand-folder: Public ordinary functions
make-subcommand-terminal: Public ordinary functions
merge-hts: Private ordinary functions
Method, %function: Private generic functions
Method, (setf %function): Private generic functions
Method, (setf interface): Private generic functions
Method, (setf key): Private generic functions
Method, (setf path): Private generic functions
Method, (setf subcommands): Private generic functions
Method, dispatch-subcommand: Private generic functions
Method, dispatch-subcommand: Private generic functions
Method, folder-is-terminal-folder: Public generic functions
Method, folder-is-terminal-path: Public generic functions
Method, interface: Private generic functions
Method, key: Private generic functions
Method, path: Private generic functions
Method, print-help: Public generic functions
Method, print-help: Public generic functions
Method, print-help: Public generic functions
Method, print-help: Public generic functions
Method, print-help-and-exit: Public generic functions
Method, print-help-and-exit: Public generic functions
Method, subcommands: Private generic functions
Method, unknown-subcommand-folder: Public generic functions
Method, unknown-subcommand-key: Public generic functions
Method, unknown-subcommand-path: Public generic functions

P
path: Private generic functions
path: Private generic functions
plist-remove-keys: Private ordinary functions
print-entry-options: Private ordinary functions
print-help: Public generic functions
print-help: Public generic functions
print-help: Public generic functions
print-help: Public generic functions
print-help: Public generic functions
print-help-and-exit: Public generic functions
print-help-and-exit: Public generic functions
print-help-and-exit: Public generic functions
print-path-help: Private ordinary functions

S
signal-folder-is-terminal: Private ordinary functions
signal-unknown-subcommand: Private ordinary functions
subcommand-folder-p: Private ordinary functions
subcommand-terminal-p: Private ordinary functions
subcommands: Private generic functions
subcommands: Private generic functions

U
unknown-subcommand-folder: Public generic functions
unknown-subcommand-folder: Public generic functions
unknown-subcommand-key: Public generic functions
unknown-subcommand-key: Public generic functions
unknown-subcommand-path: Public generic functions
unknown-subcommand-path: Public generic functions

Jump to:   %   (  
A   C   D   E   F   G   H   I   K   M   P   S   U