glimr/console/command
Glimr Console Command
Console commands need argument parsing, help output, and (for database commands) pool lifecycle management. Without a shared abstraction, every command would duplicate that boilerplate. This module provides a fluent builder API so commands declare their args and handler, and the framework handles parsing, validation, and cleanup.
Types
Separating parsed results by kind (positional, flags, options) lets accessor functions like get_arg and has_flag look up values in the right collection without ambiguity between a flag named “verbose” and an argument named “verbose”.
pub type Args {
Args(
arguments: dict.Dict(String, String),
flags: List(String),
options: dict.Dict(String, String),
)
}
Constructors
A single type for all commands keeps the registry and dispatch simple. Database commands look identical from the outside — their pool lifecycle is captured inside the handler closure by db_handler/cache_db_handler.
pub type Command {
Command(
description: String,
args: List(CommandArg),
handler: fn(Args) -> Nil,
)
}
Constructors
-
Command( description: String, args: List(CommandArg), handler: fn(Args) -> Nil, )
A union of all argument kinds keeps the args list homogeneous so commands can mix positional args, flags, and options in a single list. The parser uses the variant tag to decide how to match each CLI token.
pub type CommandArg {
Argument(name: String, description: String)
Flag(name: String, short: String, description: String)
Option(name: String, description: String, default: String)
}
Constructors
-
Argument(name: String, description: String) -
Flag(name: String, short: String, description: String) -
Option(name: String, description: String, default: String)
Values
pub fn args(cmd: Command, arguments: List(CommandArg)) -> Command
Declaring args up front lets the framework validate required arguments and generate help output automatically, so command authors don’t write their own parsing or usage strings.
Example
command.new()
|> command.args([
Argument("name", "The name of the controller"),
Flag("resource", "r", "Generate a resource controller"),
Option("template", "Template to use", "default"),
])
|> command.handler(fn(args) { ... })
pub fn cache_db_handler(
cmd: Command,
user_handler: fn(Args, db.DbPool, String) -> Nil,
) -> Command
Wraps a handler with the raw database pool behind a DatabaseStore cache — not the CachePool abstraction, but the actual SQL connection — for commands that create or alter the cache table itself. Intended for framework and third- party package commands that must work with any driver.
User commands that target a specific driver should start the
pool explicitly instead. Use make_command with a driver
option (e.g. --cache-postgres=main) to generate a stub.
pub fn cache_handler(
cmd: Command,
user_handler: fn(Args, cache.CachePool) -> Nil,
) -> Command
Wraps a handler with automatic cache pool lifecycle
management using dynamic driver dispatch. Intended for
framework and third-party package commands that must work
with any cache driver — the --cache option lets the user
select which store to use at runtime.
User commands that target a specific driver should start the
pool explicitly instead. Use make_command with a driver
option (e.g. --cache-redis=main) to generate a stub with
the correct imports and lifecycle code.
pub fn cache_option() -> CommandArg
Every cache command needs a –cache option to select which store to use. Centralizing it here keeps the default value (“_default” → first configured store) consistent.
pub fn db_handler(
cmd: Command,
user_handler: fn(Args, db.DbPool) -> Nil,
) -> Command
Wraps a handler with automatic database pool lifecycle
management using dynamic driver dispatch. Intended for
framework and third-party package commands that must work
with any database driver — the --database option lets the
user select which connection to use at runtime.
User commands that target a specific driver should start the
pool explicitly instead. Use make_command with a driver
option (e.g. --db-postgres=main) to generate a stub with
the correct imports and lifecycle code.
pub fn db_option() -> CommandArg
Every database command needs the same –database option. Centralizing it here avoids typos and keeps the default value (“_default” → first configured connection) consistent across all commands.
pub fn description(cmd: Command, description: String) -> Command
The description is the only documentation users see when listing commands. Keeping it settable via the fluent API means it stays close to the command definition rather than in a separate registry.
pub fn get_arg(parsed: Args, name: String) -> String
Required arguments are validated before the handler runs, so a missing value here is a programming error, not user input. The assert crash makes that obvious rather than silently returning a default.
pub fn get_option(parsed: Args, name: String) -> String
Options always have defaults (set in the arg definition), so missing values shouldn’t happen after parsing. Returning empty string on lookup failure is a safe fallback that avoids forcing callers to handle a Result.
pub fn handler(cmd: Command, handler: fn(Args) -> Nil) -> Command
Plain handlers receive only parsed Args — they don’t need database access. Database commands should use db_handler or cache_db_handler which inject the pool automatically.
pub fn has_flag(parsed: Args, name: String) -> Bool
Flags are boolean — present or absent. A simple Bool return is cleaner than forcing callers to match on a Result when they just need to branch on whether –verbose was passed.
pub fn new() -> Command
Starting with empty defaults lets callers build commands incrementally via pipes, setting only the fields they need. The temp_handler crashes with a clear message if someone forgets to set a real handler.
Example
command.new()
|> command.description("Greet the user")
|> command.handler(fn(args) { ... })