# How to provide nested command usage message with \`command-line\`

**URL:** <https://racket.discourse.group/t/how-to-provide-nested-command-usage-message-with-command-line/3825>\
**Category:** Questions & Answers\
**Created:** [July 1, 2025, 9:24am UTC](https://racket.discourse.group/t/how-to-provide-nested-command-usage-message-with-command-line/3825 "2025-07-01T09:24:21Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![dannypsnl](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/dannypsnl/32/917_2.png) [@dannypsnl](https://racket.discourse.group/u/dannypsnl)\
**Post date:** [July 1, 2025, 9:24am UTC](https://racket.discourse.group/t/how-to-provide-nested-command-usage-message-with-command-line/3825/1 "2025-07-01T09:24:21Z")

</div>

In Racket, we can use `command-line` to create single command with some flags.

I use

```scheme
(define args (command-line ...))
(match args
  [(list "command1") ...]
  [(list "command2") ...]
  [(list "command3") ...])

```

to create program with nested command. The problem is usage text will not mention these nested commands then.

Is that possible to use `command-line` to complete this purpose and with proper usage help?

---

<div class="post-metadata">

**Author:** ![dannypsnl](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/dannypsnl/32/917_2.png) [@dannypsnl](https://racket.discourse.group/u/dannypsnl)\
**Post date:** [July 5, 2025, 8:06am UTC](https://racket.discourse.group/t/how-to-provide-nested-command-usage-message-with-command-line/3825/2 "2025-07-05T08:06:55Z")

</div>

> <https://github.com/jackfirth/resyntax/blob/master/cli.rkt>

I think this style is good enough to be the answer.

---

<div class="post-metadata">

**Author:** ![benknoble](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/benknoble/32/16_2.png) [@benknoble](https://racket.discourse.group/u/benknoble)\
**Post date:** [July 7, 2025, 5:08pm UTC](https://racket.discourse.group/t/how-to-provide-nested-command-usage-message-with-command-line/3825/3 "2025-07-07T17:08:12Z")

</div>

Here's a version I've extracted from some unpublished code that is a bit less manual than Resyntax's CLI pattern, but that means there's more happening implicitly (IOW: tradeoffs apply):

```scheme
#lang racket

(module+ main
  (define (make-new-command-name command)
    (and (current-command-name)
         (format "~a ~a" (current-command-name) command)))

  (define (run name c)
    (parameterize ([current-command-name (make-new-command-name name)])
      ((command-invoke c))))

  (define padding-length
    (add1 (apply max (map symbol-length (hash-keys command-table)))))

  (parse-command-line
   (short-program+command-name)
   (current-command-line-arguments)
   `((usage-help "This tool is used to <do stuff>."
                 "Command is one of:"
                 ,@(for/list ([(name c) (in-hash command-table)])
                     (format " ~a:~a\t~a"
                             name
                             (make-padding-string (symbol-length name) padding-length)
                             (command-description c)))))
   (λ (_acc name-string . args)
     (define name (string->symbol name-string))
     (cond
       [(hash-has-key? command-table name)
        (parameterize ([current-command-line-arguments (list->vector args)])
          (run name (hash-ref command-table name)))]
       [else
        (eprintf "~a: unknown command ~a\n" (short-program+command-name) name)
        (eprintf "Expected one of: ~a\n"
                 (string-join (map symbol->string (hash-keys command-table)) ", "))
        (exit 1)]))
   '("command" "command-arguments")))

(require raco/command-name
         syntax/parse/define
         file/glob
         db
         axio
         threading)

;; planet/private/command implements an svn-style command-line macro; for now,
;; we'll stick with something a bit simpler
(struct command (description invoke))
(define command-table (make-hash))
(define-syntax-parser define/command
  [(_ name:id description:string body:expr ...+)
   (syntax/loc this-syntax
     (begin
       (define (name)
         body ...)
       (hash-set! command-table 'name (command description name))))])

(define/command init "Initialize the <thing>"
  (define opt-result "default")
  (command-line
   #:program (short-program+command-name)
   #:once-each
   [("--opt") --opt-value "Help for --opt [default]"
              (set! opt-result --opt-value)]
   #:args (positional)
   (exit (do-init opt-result positional))))

(define (symbol-length s)
  (string-length (symbol->string s)))

(define (make-padding-string occupied-length padding-length)
  (make-string (- padding-length occupied-length) #\space))

;; vim: lispwords+=define/command

```

This code was used in a custom `raco` tool, so the `raco/command-name` and its exports _may_ not be relevant for your use case.
