lcli

Build Status][gh-actions-badge]][gh-actions]
[![LFE Versions][lfe badge]][lfe]
[![Erlang Versions][erlang badge] [ Tags

An LFE Command Line Options & Tool Builder

Project logo

Table of Contents

NOTICE: This is a work in progress

Introduction

lcli (pronounced "Elk-ly") is a semi-opinionated library for creating command line tools and supporting options (flags) and arguments (non-flag parameters) parsing. Regular options parsing is done using the Erlang getopt community library. lcli manages the handling of commands and nested subcommands.

lcli is very opinionated around context: sometimes the complete context for a command is needed, even down at the lowest (most-nested) level. As such, this should always be made available to the programmer/script writer. (This is a lesson-learned from some of the Common Lisp command line pasring libraries which don't provide the entire context at any given time -- something that can lead to much awkward code.)

lcli is not opinionated about how option and argument values are to be handled -- possibly not even with a function itself. As such, the specs do not contain or require one to add functions for handling particular options. It is intended that these decisions are managed by whatever main function (or set of dispatched functions) is (are) in charge of the script itself. This provides the programmer/scripter with maximum flexibility and minimum fuss.

Erlang getopt Wrapper and More

lcli provides a very thing wrapper around getopt, basically converting to and from the getopt spec tuple with LFE/Erlang maps.

In addition, lcli provides command and subcommand support where the command spec is a superset of the getopt spec (thus allowing us to use the getopt library transparently).

With lcli, you have the option of defining specs as maps or as getopt specs; each has its own aestheic tradeoffs. Note that with maps, the name is optional (taken from the long option name if missing).

Example map specs:

'(#m(long "host" short #\h type string default "localhost"
     help "Database server host")
  #m(long "port" short #\p type integer
     help "Database server port")
  #m(long "dbname" type string default "users"
     help "Database name")
  #m(name xml short #\x help "Output data in XML")
  #m(long "verbose" short #\v type integer help "Verbosity level")
  #m(name file type string help "Output file"))

Example getopt specs:

'(#(host #\h "host" #(string "localhost") "Database server host")
  #(port #\p "port" integer "Database server port")
  #(dbname undefined "dbname"#(string "users") "Database name")
  #(xml #\x "xml" undefined undefined "Output data in XML")
  #(verbose #\v "verbose" integer "Verbosity level")
  #(file undefined undefined string "Output file"))

Differences from lfescript

This library does not use lfescript as its basis, but rather the lfe executable itself. As such, a main function is not run automatically. Instead, the final line of the script must call the entrypoint function.

Example Usage

Wrapping getopt

A simple script below demonstrates lcli's wrappage of the Erlang getopt library:

#!/usr/bin/env lfe

(defun options ()
  '(#m(long "help" help "Display help text")
    #m(long "host" short #\h type string default "localhost" help "Database server host")
    #m(long "port" short #\p type integer help "Database server port")
    #m(long "dbname" type string default "users" help "Database name")
    #m(name xml short #\x help "Output data in XML")
    #m(long "verbose" short #\v type integer help "Verbosity level")
    #m(long "output" short #\o type string help "Output file")))

(defun main ()
  (case (lcli:parse (options))
    (`(,_ #(opts #m(help true)) ,_ ,_)
     (lcli:usage (options) "db.lfe")
     (halt 0))
    (result
     (lfe_io:format "~p~n" `(,result))
     (halt 0)))
  'ok)

(main)

Test run:

$ ./examples/db.lfe -x --port 5099 --dbname webapp -o output.dump arg1 arg2

Output:

(#(cmd "./examples/db.lfe")
 #(opts
   #M(dbname "webapp" host "localhost" output "output.dump"
      port 5099 xml true))
 #(args ("arg1" "arg2"))
 #(cmds undefined))

Help:

 $ ./examples/db.lfe --help

Output:

Usage: db.lfe [--help] [-h [<host>]] [-p <port>] [--dbname [<dbname>]]
              [-x] [-v <verbose>] [-o <output>]

  --help         Display help text
  -h, --host     Database server host [default: localhost]
  -p, --port     Database server port
  --dbname       Database name [default: users]
  -x             Output data in XML
  -v, --verbose  Verbosity level
  -o, --output   Output file

Subcommands

More sophisticated usage can create subcommands, etc., for creating command line tools with potentially vast arrays of functionality:

TBD

Documentation

Project documentation is TBD.

Example scripts (some not using lcli, for demonstration purposes) are available here:

If you've created a simple script that shows off some nice functionality of "Elkly", you are encouraged to submit a PR for inclusion here!

License

Copyright © 2016-2023 Duncan McGreggor

Distributed under the Apache License, Version 2.0.