commit c5a2bb56b294229b5d71747484648da78bc68ac6 Author: hellerve Date: Mon Oct 14 20:29:39 2019 +0200 initial diff --git a/README.md b/README.md new file mode 100644 index 0000000..e98d2a5 --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# cli + +A simple CLI library for Carp. + +```clojure +(load "git@github.com:carpentry-org/cli.carp.git@master") + +(defn main [] + (let [p (=> (CLI.new @"My super cool tool!") + (CLI.add &(CLI.option "--flag" "-f" "my flag" true)))] + (match (CLI.parse &p) + (Result.Success p) (IO.println &(str &(CLI.get &p "--flag"))) + (Result.Error msg) (do (IO.errorln &msg) (CLI.usage &p))))) +``` + +## Usage + +```clojure +(load "git@github.com:carpentry-org/cli.carp.git@master") +``` + +
+ +Have fun! diff --git a/cli.carp b/cli.carp new file mode 100644 index 0000000..eba8745 --- /dev/null +++ b/cli.carp @@ -0,0 +1,96 @@ +(defmodule CLI + (deftype Option [ + long String + short String + description String + required Bool + default (Maybe String) + options (Maybe (Array String)) + ]) + + (deftype Parser [ + description String + options (Array Option) + values (Map String (Maybe String)) + ]) + + (defn new [descr] (Parser.init descr [] {})) + + (defn add [p opt] + (Parser.update-values + (Parser.update-options p &(fn [options] (Array.push-back options @opt))) + &(fn [values] (Map.put values (Option.long opt) &(Maybe.Nothing))))) + + (defndynamic option- [long short description required default-options] + (if (= (length default-options) 0) + (list 'CLI.Option.init + (list 'copy long) (list 'copy short) (list 'copy description) + required '(Maybe.Nothing) '(Maybe.Nothing)) + (if (= (length default-options) 1) + (list 'CLI.Option.init + (list 'copy long) (list 'copy short) (list 'copy description) + required (list 'Maybe.Just (car default-options)) '(Maybe.Nothing)) + (list 'CLI.Option.init + (list 'copy long) (list 'copy short) (list 'copy description) + required (list 'Maybe.Just (car default-options)) + (list 'Maybe.Just (cadr default-options)))))) + + (defmacro option [long short description required :rest default-options] + (CLI.option- long short description required default-options)) + + (defn options-str [p] + (String.join + "|" + &(Array.copy-map + &(fn [o] (fmt "%s|%s" (Option.short o) (Option.long o))) + (Parser.options p)))) + + (defn usage [p] + (do + (IO.println + &(fmt "usage: %s [%s]\n%s\nOptions:" + (System.get-arg 0) &(options-str p) (Parser.description p))) + (foreach [arg (Parser.options p)] + (do + (IO.print + &(fmt " %s|%s: %s" + (Option.long arg) (Option.short arg) (Option.description arg))) + (when @(Option.required arg) (IO.print " REQUIRED")) + (when (Maybe.just? (Option.default arg)) + (IO.print &(fmt " (default: %s)" &(Maybe.unsafe-from @(Option.default arg))))) + (when (Maybe.just? (Option.options arg)) + (IO.print &(fmt " (options: %s)" &(String.join ", " &(Maybe.unsafe-from @(Option.options arg)))))) + (IO.println ""))) + (IO.println " --help|-h: print this help message and exit."))) + + (defn parse [p] + (let-do [values (Parser.values p) + res (Result.Success @p)] + (for [i 1 (System.get-args-len)] + (let [x (System.get-arg i)] + (if (Map.contains? values x) + (do + (set! i (Int.inc i)) + (if (< i (System.get-args-len)) + (Map.put! values x &(Maybe.Just @(System.get-arg i))) + (do + (set! res (Result.Error (fmt "No value for: %s" x))) + (break)))) + (if (or (= x "--help") (= x "-h")) + (do + (usage p) + (System.exit 0)) + (do + (set! res (Result.Error (fmt "Unknown option: %s" x))) + (break)))))) + (match res + (Result.Success _) (Result.Success @p) + (Result.Error x) (Result.Error x)))) + + (defn values [p] + @(Parser.values p)) + + (defn get [p k] + (let [m (values p)] + (Map.get-with-default &m k &(Maybe.Nothing)))) +) diff --git a/examples/simple.carp b/examples/simple.carp new file mode 100644 index 0000000..c964809 --- /dev/null +++ b/examples/simple.carp @@ -0,0 +1,9 @@ +(load "cli.carp") + +(defn main [] + (let [p (=> (CLI.new @"My super cool tool!") + (CLI.add &(CLI.option "--flag" "-f" "my flag" true)) + (CLI.add &(CLI.option "--thing" "-t" "my thing" false @"hi" [@"a" @"b" @"hi"])))] + (match (CLI.parse &p) + (Result.Success p) (IO.println &(str &(CLI.get &p "--flag"))) + (Result.Error msg) (do (IO.errorln &msg) (CLI.usage &p)))))