= Crytal Cli Generator :author: Tristan Anclelet :email: tristanancelet@yahoo.com :toc: This document outlines the overall design of the CliGen shard & it's 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. [source,crystal] ---- 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, 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 puts "Total : %i" % output end end CliGen::App.process 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 == Planned Features === Markdown Documentation Generation Since all command metadata is present in annotations at compile time (`@[CliGen::CommandInfo]`, `@[CliGen::SubCommand]`, `@[CliGen::Argument]`, `@[CliGen::Selection]`), the framework can walk the same structures that `generate.cr` already walks and render them into a Markdown document instead of a `CommandNode` tree. The generation would be driven by a `macro finished` block (similar to `generate.cr`) that emits a `self.generate_docs` class method on `App`. This method walks every `Command` subclass and its annotations to produce a structured document. Proposed output structure: ---- # ## Commands ### mycommand #### Flags | Flag | Short | Type | Default | Description | |------|-------|------|---------|-------------| | --myvar VAR | -m | Int32 | 23 | ... | #### Subcommands - `do_thing` — - Examples: ... ---- Implementation notes: * Driven by a `--generate-docs` global flag or a dedicated class method * ECR templates (already pulled in) are the natural rendering mechanism * The same annotation data powers both runtime help output and the doc generator, keeping them in sync automatically === Bash Autocompletion Generation Since all command names and flag names are known at compile time, a complete bash completion script can be generated statically — no runtime `--completions` endpoint needed. The approach is a hidden `--generate-completion bash` flag (potentially extended to `zsh`/`fish` later) that prints a ready-to-install completion script to stdout. Proposed completion script shape: [source,bash] ---- _myapp() { local cur="${COMP_WORDS[COMP_CWORD]}" local prev="${COMP_WORDS[COMP_CWORD-1]}" case "${COMP_WORDS[1]}" in mycommand) COMPREPLY=($(compgen -W "--myvar -m --format -f" -- "$cur")) ;; *) COMPREPLY=($(compgen -W "mycommand myothercommand" -- "$cur")) ;; esac } complete -F _myapp myapp ---- Implementation notes: * Script body generated at compile time via a `macro finished` walk of `Command.subclasses` * `@[CliGen::Selection]` options (`%w[json yaml ecr]`) can be included as valid completions for their flag * Install path: `myapp --generate-completion bash > ~/.bash_completion.d/myapp` or printed with instructions * Same annotation data used by the doc generator, so both stay in sync with the command definition