Commander
Create command line applications.
module main
import khalyomede.commander { Command, TerminatingFlag, Argument }
import os
fn main() {
mut command := Command{
input: os.args
name: "greet"
flags: [
TerminatingFlag{
name: "help"
execute: fn (mut command Command) i8 {
return command.help()
}
}
],
arguments: [
Argument{
name: "name"
description: "Your first name."
}
]
execute: fn (mut command Command) i8 {
name := command.argument("name") or { "Stranger" }
println("Hello ${name}!")
return 0
}
}
command.serve()
}
> ./main John
Hello John!
Summary
About
I create this library to help me create command line applications and tools. I needed a way to focus on my business logic, with a tool that help me parse and get parameters, flags and arguments.
Features
- Supports Flags, to customize your command behavior (like
--quiet
or -q
) - Supports Terminating Flags, which are flags that will interrupt the normal execution of your command (like
--help
or -h
) - Supports Parameters, which are flags that take values (like ̀
--region=euw
or -r=euw
) - Supports both equal separated and space separated parameters (
--mode=parallel
or --mode parallel
works interchangeably) - For Flags and Parameters, supports long form (two dashes
--help
) and short form (single dash -h
) - Supports single and list of arguments
- Supports auto generated help documentation
- Supports subcommands (like
greet John
would be the main command and greet install --quiet
would be the subcommand with the same features as mentioned above) - Supports validating Arguments, Parameters and Flags on a separated step within the definition
- Supports returning both positive and negative return codes (from -255 to 255)
- Returns a
Result
so you can control the exit step and easily unit test your commands - Easy output unit testing thanks to customized
println
and eprintln
Installation
Using V installer
Run this command in your project root folder terminal:
v install khalyomede.commander
Manual download
- Select the branch/tag of your choice
- Download the zip of the repository (green button, then below "Download ZIP")
- Locate your V modules path (usually this will be the folder at "~/.vmodules")
- Inside this V modules folder, create a folder called "khalyomede"
- Inside this "khalyomede" folder, create another folder called "commander"
- Unzip the content of the folder inside the "~/.vmodules/khalyomede/commander" folder
You should end up with a folder tree like this:
~/.vmodules
└── khalyomede
└── commander
├── Argument_is_filled.v
├── Argument.v
├── ...
└── v.mod
Differences between arguments, flags and parameters
Arguments
These are the values you provide by default to the command. For example if your command is an AI agent expecting a message, an argument will typically be the right component to use.
Flags
These are made to customize the behavior of your command. They accept no values. You can only check for the presence (or absence) of a flag. For example, if you display additional debug information, a
--quiet
Parameters
These are elements that will customize the behavior of your command, which accept values. For example, if your command deploys a V application on a VPS (cloud server), and by default you deploy on a European (euw-1) zone, you could accept a
--region
Examples
- Hello world
- Display help documentation
- Arguments
- Flags
- Parameter
- Testing
- Create sub commands
Hello world
module main
import khalyomede.commander { Command }
import os
fn main() {
mut command := Command{
input: os.args
name: "greet"
description: "Greet the user."
execute: fn (mut command Command) i8 {
println("Hello world!")
return 0
}
}
command.serve()
}
Display help documentation
Use
command.help()
--help
-h
module main
import khalyomede.commander { Command, TerminatingFlag }
import os
fn main() {
mut command := Command{
input: os.args
name: "greet"
description: "Greet the user."
flags: [
TerminatingFlag{
name: "help"
short_name: "h"
description: "Show the documentation."
execute: fn (mut command Command) i8 {
return command.help()
}
}
]
execute: fn (mut command Command) i8 {
println("Hello world!")
return 0
}
}
command.serve()
}
Simple argument
Argument are the basic information passed after the command name.
module main
import khalyomede.commander { Command, Argument }
import os
fn main() {
mut command := Command{
input: os.args
name: "greet"
description: "Greet the user."
arguments: [
Argument{
name: "person"
description: "The name of the person to greet"
}
]
execute: fn (mut command Command) i8 {
person := command.argument("person") or { "World" }
println("Hello ${person}!")
return 0
}
}
command.serve()
}
### Argument list
You can catch all argument in the form of a list using an ArgumentList.
module main
import khalyomede.commander { Command, ArgumentList }
import os
fn main() {
mut command := Command{
input: os.args
name: "greet"
description: "Greet the user."
arguments: [
ArgumentList{
name: "friends"
description: "The name of the friends to greet."
}
]
execute: fn (mut command Command) i8 {
friends := command.arguments("friends") or { ["World"] }
friends_list := friends.join(", ")
println("Hello ${friends_list}!")
return 0
}
}
command.serve()
}
Getting an argument value
For simple Argument, use
command.argument()
command.arguments()
module main
import khalyomede.commander { Command, Argument, ArgumentList }
import os
fn main() {
mut command := Command{
input: os.args
name: "greet"
description: "Greet the user."
arguments: [
Argument{
name: "person"
description: "The name of the person to greet."
}
ArgumentList{
name: "friends"
description: "The name of the friends to greet."
}
]
execute: fn (mut command Command) i8 {
person := command.argument("person") or { "Stranger" }
friends := command.arguments("friends") or { ["nobody else"] }
friends_list := friends.join(", ")
println("Hello ${person} and ${friends_list}!")
return 0
}
}
command.serve()
}
Validate an argument
Note that when the command validation fails, it automatically returns an exit code 1.
module main
import khalyomede.commander { Command, Argument }
import os
fn main() {
mut command := Command{
input: os.args
name: "greet"
description: "Greet the user."
arguments: [
Argument{
name: "person"
description: "The name of the person to greet."
validate: fn (mut command Command) ! {
person := command.argument("person") or { "" }
if person.len == 0 {
return error("The person argument is required.")
}
}
}
]
execute: fn (mut command Command) i8 {
person := command.argument("person") or { "Stranger" }
println("Hello ${person}!")
return 0
}
}
command.serve()
}
Add a flag
A flag is a simple trigger that helps you customize the behavior of your command according to the presence of absence of this flag.
module main
import khalyomede.commander { Command, Flag }
import os
fn main() {
mut command := Command{
input: os.args
name: "greet"
description: "Greet the user."
flags: [
Flag{
name: "quiet"
short_name: "q"
description: "Display less debug information."
}
]
execute: fn (mut command Command) i8 {
if !command.has_flag("quiet") {
println("Starting...")
}
println("Hello world!")
if !command.has_flag("quiet") {
println("Command finished.")
}
return 0
}
}
command.serve()
}
Add a terminating flag
Terminating flag interrupt the program when present, by specifying a custom termination code.
module main
import khalyomede.commander { Command, TerminatingFlag }
import os
fn main() {
mut command := Command{
input: os.args
name: "greet"
description: "Greet the user."
flags: [
TerminatingFlag{
name: "version"
short_name: "v"
description: "Display the current version."
execute: fn (mut command Command) i8 {
println("v0.1.0")
return 0
}
}
]
execute: fn (mut command Command) i8 {
println("Hello world!")
return 0
}
}
command.serve()
}
Check if a flag is present
Use
command.has_flag("name")
module main
import khalyomede.commander { Command, Flag }
import os
fn main() {
mut command := Command{
input: os.args
name: "greet"
description: "Greet the user."
flags: [
Flag{
name: "quiet"
short_name: "q"
description: "Display less debug information."
}
]
execute: fn (mut command Command) i8 {
if !command.has_flag("quiet") {
println("Starting...")
}
println("Hello world!")
return 0
}
}
command.serve()
}
Add a parameter
module main
import khalyomede.commander { Command, Parameter }
import os
fn main() {
mut command := Command{
input: os.args
name: "greet"
description: "Greet the user."
parameters: [
Parameter{
name: "region"
short_name: "r"
description: "Greet on the given region."
}
]
execute: fn (mut command Command) i8 {
println("Hello world")
return 0
}
}
command.serve()
}
Get the value of a parameter
module main
import khalyomede.commander { Command, Parameter }
import os
fn main() {
mut command := Command{
input: os.args
name: "greet"
description: "Greet the user."
parameters: [
Parameter{
name: "region"
short_name: "r"
description: "Greet on the given region."
}
]
execute: fn (mut command Command) i8 {
region := command.parameter("region") or { "World" }
println("Hello {$region$}!")
return 0
}
}
command.serve()
}
Validate a parameter
Note that when the command validation fails, it automatically returns an exit code 1.
module main
import khalyomede.commander { Command, Parameter }
import os
fn main() {
mut command := Command{
input: os.args
name: "greet"
description: "Greet the user."
parameters: [
Parameter{
name: "region"
short_name: "r"
description: "Greet on the given region."
validate: fn (mut command Command) ! {
region := command.parameter("region") or {
return error("Missing parameter --region.")
}
if !["World", "Paris"].contains(region) {
return error("Parameter --region must be either World or Paris.")
}
}
}
]
execute: fn (mut command Command) i8 {
region := command.parameter("region") or { "World" }
println("Hello {$region$}!")
return 0
}
}
command.serve()
}
Parameter default value
module main
import khalyomede.commander { Command, Parameter }
import os
fn main() {
mut command := Command{
input: os.args
name: "greet"
description: "Greet the user."
parameters: [
Parameter{
name: "region"
short_name: "r"
description: "Greet on the given region."
default: "Universe"
}
]
execute: fn (mut command Command) i8 {
region := command.parameter("region") or { "" } // Default to "Universe"
println("Hello from ${region}!")
return 0
}
}
command.serve()
}
### Parameter with allowed values
You can specify a list of allowed values for a parameter. If the user provides a value that's not in the allowed list, the command will return an error with exit code 1.
module main
import khalyomede.commander { Command, Parameter }
import os
fn main() {
mut command := Command{
input: os.args
name: "greet"
description: "Greet the user."
parameters: [
Parameter{
name: "region"
short_name: "r"
description: "The region to greet on."
allowed: ["euw-1", "euw-2", "euw-3"]
}
]
execute: fn (mut command Command) i8 {
region := command.parameter("region") or { "euw-1" }
println("Hello from ${region}!")
return 0
}
}
command.serve()
}
Testing the output
You can use
command.println()
command.print_error()
println()
eprintln()
module test
import khalyomede.commander { Command, Argument }
pub fn test_it_greet_user() {
mut command := Command{
input: [@FILE, "John"]
name: "greet"
description: "Greet the user."
arguments: [
Argument{
name: "person"
description: "The person to greet."
}
]
execute: fn (mut command Command) i8 {
person := command.argument("person") or { "World" }
command.println("Hello ${person}!")
return 0
}
}
result := command.run()
assert result.output == "Hello John!\n"
}
Testing the exit code
module test
import khalyomede.commander { Command, Argument }
pub fn test_it_returns_error_when_name_is_not_passed() {
mut command := Command{
input: [@FILE]
name: "greet"
description: "Greet the user."
arguments: [
Argument{
name: "person"
description: "The name of the person to greet."
validate: fn (mut command Command) ! {
person := command.argument("person") or { "" }
if person.len == 0 {
return error("Argument person is required.")
}
}
}
]
execute: fn (mut command Command) i8 {
person := command.argument("person") or { "World" }
command.println("Hello ${person}!")
return 0
}
}
result := command.run()
assert result.exit_code == 1
}
Create sub commands
You can declare sub commands, that will have the same capabilities as mentioned above.
For example you can have a base command
greet John
greet install --quiet
module main
import khalyomede.commander { Command }
import os
fn main() {
mut command := Command{
input: os.args
name: "greet"
description: "Greet the user."
commands: [
Command{
name: "install"
description: "Install the greet command."
execute: fn (mut command Command) i8 {
println("Installing...")
return 0
}
}
]
execute: fn (mut command Command) i8 {
println("Hello world!")
return 0
}
}
command.serve()
}