Files
cligen/design.adoc
T
2026-08-09 15:20:06 -05:00

2.7 KiB
Raw Blame History

Crytal Cli Generator

This document outlines the overall design of the CliGen shard & its underlying classes/objects & their usecases.

Architecture

The desire for this project is to provide a framework for generating commandline arg-parses & command dispatch built into a class/object.

The idea is to (much like JSON::Serializable & YAML::Serializable) is to use macros to help defining a Command object and abstract the command-line away from the codebase needing input from the user at init time.

Command Objects

The core of this codebase is the CliGen::Command object.

This is the object that is meant to provide you the hook into being able to utilize the codebase. It is where you are able to define all of your expecetd arguments/flags & any selectables (aka, instance variable that you want the user to choose one value for. ex: -f|--format json|yaml|ecr).

To make this all possible we load the base class with macros that help define your variables & inform the framework how it needs to handle arguments being presented to your command object setters.

require "cligen"

module MyModule
  class MyCommand < CliGen::Command
    argument(myvar : Int32 = 23,
      long: "--myvar VAR",
      short: "-m",
      description: "This tells the utility how many times to do thing",
      validate: ->(v : Int32) : Bool { (1..23).includes?(v) }
    )

    selection(output_format : String = "ecr",
      long: "--format FORMAT",
      short: "-f",
      description: "Inform the utility what output you want the data in",
      options: %w[ json yaml ecr ]
    )

    DO_THING_EXAMPLES = [
      "myutil mycommand do_thing --myvar 5"
    ]

    subcommand do_thing : Int32,
      description: "Do the THING",
      examples: MyModule::MyCommand::DO_THING_EXAMPLES \
    do
      output = 0

      @myvar.times do |i|
        puts "thing done %i times" % [ i + 1 ]
        output += i
      end

      output
    end
  end

  CliGen::App.process(ARGV)
end

The examples like above provide a "DSL-esk" way of defining: - Instance Variables, - Short/Long flags - Description of the flags (used in the help output as well) - A verification proc/lambda for doing ad-hoc checks of the value provided by the user (essentially allowing you to implement your own option: key like in selection) - Selections (currently compile-time and will open it up to runtime collecting of options later on based on defined annotations in the class) - define subcommands of this current command

How it works

Using crystal macros, you define the shape (arguments/flags, selections, work functions/subcommands, etc