Skip to content

Building CLI Tools

Example

example/download.dol downloads files concurrently with progress reporting:

#!/usr/bin/env -S dolang --strict
import args progress http fs url strand

def download client url
  let name = url.name
  if !name
    throw "no identifiable file name: $url"
  client.get $url do |resp|
    let len = resp.headers.get content-length
    let success = false
    try
      fs.open $name w do |file| progress.show
        message: $name
        total: (len && int len)
        units: :BYTES:
        do |w| for chunk = resp.chunks()
          file.write $chunk
          w.delta $chunk.len
      success = true
    finally
      if !success
        fs.remove $name ignore: true

args.with
  help: Download files over HTTP(S)
  - opt: limit
    short: l
    parse: $int
    help: Maximum concurrent downloads
    default: 4
  - arg: urls
    collect: true
    help: URLs to download
    parse: $url.Url
  do |args| progress.with do
    let client = http.Client()
    strand.pool $args.limit $args.urls do |url|
      download $client $url

Argument Parsing

args.with describes the command line and invokes its block with a record of converted values. parse: specifies how to parse the argument from a string, e.g. parse: $url.Url ensures that download receives URL objects. collect: true gathers the remaining positional arguments into an array.

Progress Indicators

progress.with activates progress rendering for the passed block scope. progress.show creates a child indicator.

Structured Concurrency

strand.pool feeds URLs lazily to a worker pool of args.limit strands. The call is scoped, waiting for all work to finish (or an uncaught error to propagate, in which case remaining workers are canceled) and cleaning up the pool on exit.