Argus
A powerful, reflection-based CLI argument parser library for Go. Build structured, type-safe command-line applications with minimal boilerplate.

Features
- Struct-based binding — Use Go struct fields and tags to declaratively define CLI arguments, flags, and positional parameters
- Type safety — Automatic parsing and validation of int, float64, bool, string, and slice types
- Reflection-powered — No code generation; struct tags do all the work
- Flexible argument handling — Support for named flags, positional arguments, optional parameters, defaults, and array types
- Auto-generated help — Leverage field descriptions to build informative help text
- Localization & customization — Override all user-facing messages for localization or custom branding
- Dependency injection — Mock CLI input and output for easy testing without
os.Args
- Clean error handling — Structured validation errors that guide users to correct usage
Quick Example
package main
import (
"fmt"
"os"
"github.com/MateusMoutinhoOrg/Argus/adapters/native"
"github.com/MateusMoutinhoOrg/Argus/pkg/argus"
)
type ServeEntries struct {
Host string `type:"Flag" identifiers:"-h,--host" default:"localhost"`
Port int `type:"Flag" identifiers:"-p,--port" default:"8080"`
TLS bool `type:"Flag" identifiers:"--tls"`
}
func serve(e ServeEntries) int {
scheme := "http"
if e.TLS {
scheme = "https"
}
fmt.Printf("Listening on %s://%s:%d\n", scheme, e.Host, e.Port)
return 0
}
func main() {
a := argus.New(native.New())
props := argus.GenerationProps{
Callbacks: []argus.Callback{
{
Starter: "serve",
Callback: serve,
Description: "Start the application server",
},
},
}
exitCode, err := a.HandleCli(props)
if err != nil {
fmt.Println("Error:", err)
os.Exit(1)
}
os.Exit(exitCode)
}
Usage:
go run main.go serve --host 0.0.0.0 -p 3000 --tls
# Output: Listening on https://0.0.0.0:3000
Table of Contents
Getting Started
Core Concepts
Examples
Explore real working examples in the samples/ directory:
- flags/ — Named flags with defaults and boolean presence flags
- positional/ — Positional argument handling with
Arg and NextArg
- arrays/ — Array arguments with
ArrayFlag and ArrayArg
- mixed/ — Combining flags and positional arguments in one command
- gitlike/ — Multi-command pattern (subcommands like
git commit)
- types/ — Type conversions (int, float64, bool, string)
- custom_errors/ — Localized error messages (Portuguese example)
Run any sample:
go run samples/flags/flags.go serve --host 0.0.0.0 -p 9090
go run samples/positional/positional.go
go run samples/custom_errors/custom_errors.go greet "Your Name"
Installation
go get github.com/MateusMoutinhoOrg/Argus
Architecture Overview
Core Components
-
pkg/Argus/ — Core parsing engine using Go reflection
handle.go — Main CLI parsing logic; struct tag inspection, flag/positional extraction, help generation
new.go — Factory for creating Argus instances
errors.go — Error message templates (being refactored to messages.go)
-
pkg/deps/ — Dependency injection layer
deps.go — Deps interface with Args and Print for testable CLI interactions
-
adapters/native/ — OS integration
- Reads
os.Args[1:] and prints to stdout
Parsing Flow
- Inspect callback function parameter struct; read struct tags
- Extract named flags from CLI arguments (first pass)
- Populate positional arguments from remaining arguments (second pass)
- Validate required fields, apply defaults, check constraints
- Invoke callback with populated struct; capture exit code
Common Use Cases
1. Simple Command with Flags
type DeployEntries struct {
Env string `type:"Flag" identifiers:"-e,--env" default:"staging"`
Force bool `type:"Flag" identifiers:"-f,--force"`
}
func deploy(e DeployEntries) int {
fmt.Printf("Deploying to %s (force=%v)\n", e.Env, e.Force)
return 0
}
2. Multi-Command Application
props := argus.GenerationProps{
Callbacks: []argus.Callback{
{Starter: "serve", Callback: serve, Description: "Start server"},
{Starter: "build", Callback: build, Description: "Build project"},
{Starter: "test", Callback: test, Description: "Run tests"},
},
}
3. Positional Arguments
type CopyEntries struct {
Src string `type:"NextArg" help:"Source file"`
Dst string `type:"NextArg" help:"Destination file"`
Force bool `type:"Flag" identifiers:"-f,--force"`
}
func copy(e CopyEntries) int {
fmt.Printf("Copying %s → %s\n", e.Src, e.Dst)
return 0
}
4. Testing with Dependency Injection
import (
"github.com/MateusMoutinhoOrg/Argus/pkg/deps"
)
func TestServe(t *testing.T) {
testDeps := deps.Deps{
Args: []string{"serve", "--port", "9090"},
Print: func(s string) { /* capture output */ },
}
a := argus.New(&testDeps)
exitCode, _ := a.HandleCli(props)
if exitCode != 0 {
t.Fatal("serve command failed")
}
}
Tag System Reference
Each struct field declares how it's populated via tags:
| Tag |
Values |
Example |
type |
Flag, Arg, NextArg, ArrayFlag, ArrayArg |
type:"Flag" |
identifiers |
Comma-separated flag aliases |
identifiers:"-p,--port" |
position |
Index for Arg type |
position:"0" |
required |
"true" or "false" |
required:"false" |
default |
Default value as string |
default:"8080" |
start, end |
Array bounds (for ArrayArg) |
start:"0" end:"-1" |
min_size, max_size |
Array size constraints |
min_size:"1" max_size:"10" |
help |
Help text for this argument |
help:"Port number to listen on" |
See docs/entries.md for complete details.
Supported Types
- Scalars —
string, int, int64, float64, bool
- Slices —
[]string, []int, []int64, []float64 (for ArrayFlag and ArrayArg)
Error Handling
Argus validates arguments and produces user-friendly errors:
- Missing required flag → usage error with flag description
- Unparseable value (e.g., non-numeric for
int) → usage error with type info
- Missing required positional argument → usage error with position/description
- Array size constraints violated → usage error with bounds
All error messages are customizable via the Messages struct. See docs/msg_format.md.
Testing
Since Argus uses dependency injection, testing is straightforward:
func runCLI(args []string) (int, string) {
var output strings.Builder
testDeps := deps.Deps{
Args: args,
Print: func(s string) { output.WriteString(s) },
}
a := argus.New(&testDeps)
exitCode, _ := a.HandleCli(props)
return exitCode, output.String()
}
// In tests:
exitCode, output := runCLI([]string{"serve", "--port", "9090"})
if exitCode != 0 {
t.Errorf("serve failed: %s", output)
}
See docs/deps.md for comprehensive testing patterns.
Design Philosophy
- Reflection over codegen — Struct tags + reflection = zero build-time overhead
- Explicit over implicit — Tags make intent clear; no magic defaults
- Testing first — Dependency injection built in from the start
- Validation upfront — Errors surface at configuration time, not parse time
- Open-ended by default — Array arguments are unbounded; constrain with
min_size/max_size
Known Limitations & Future Work
- No automated tests — Consider adding test suite once API stabilizes
- Single-level commands — Currently one level of nesting; samples show patterns for deeper trees
- Flag syntax — Currently only
--flag value (space-separated); --flag=value not yet supported
- Negative numbers —
-5 in context of int field may be ambiguous with flag syntax
See docs/entries.md for open questions and design decisions.
License
MIT
Contributing
Contributions welcome! Please ensure:
- Struct tags are well-documented
- Examples in
samples/ demonstrate each feature
- Docs are updated for new capabilities
- Tests cover the happy path and error cases
FAQ
Q: Do I need to export struct fields?
A: Yes, Argus uses reflection and can only access exported fields. The examples use capitalized field names.
Q: Can I combine multiple entry types in one struct?
A: Yes! Flags are extracted first, then positional arguments fill the remaining tokens.
Q: How do I handle subcommands deeper than one level?
A: Argus handles single-level commands natively. For deeper nesting, write a wrapper command that parses the next level manually. See samples/gitlike/ for patterns.
Q: Can I customize the help text format?
A: Not directly via the API yet, but you can override the Messages.Help* functions to control output (see docs/msg_format.md).
Start with the Quick Start guide or explore the samples.