clibind

package module
v0.0.0-...-82a4bae Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 7, 2025 License: MIT Imports: 9 Imported by: 0

README

urfave-cli-bind

Go Reference

A tiny helper that maps urfave/cli/v3 flags to and from Go structs. Define your configuration once with struct tags, generate the corresponding cli.Flags, and bind parsed values back into the struct inside your command action.

This repository is an add-on for github.com/urfave/cli/v3. It layers reflection helpers on top of the original CLI runtime and is neither a fork nor a replacement for urfave/cli itself.

Features

  • Reflect-based flag generation via FlagsFromStruct for primitives, durations, times, UUIDs, and slices
  • Tag-driven defaults (cliDefault), usage strings (cliUsage), prefixes for nested structs (cliPrefix), and omitempty
  • Works with concrete structs or pointers, including anonymous/embedded structs for flattening
  • Binds directly from *cli.Command using the same metadata so there is no duplicate wiring

Installation

go get github.com/eosproject/urfave-cli-bind

Quick start

package main

import (
    "context"
    "log"
    "time"

    "github.com/urfave/cli/v3"
    clibind "github.com/eosproject/urfave-cli-bind"
)

type Config struct {
    Name  string        `cli:"name,n"  cliDefault:"guest" cliUsage:"User name"`
    Count int           `cli:"count"   cliDefault:"3"     cliUsage:"How many"`
    Delay time.Duration `cli:"delay"   cliDefault:"250ms" cliUsage:"Wait duration"`
}

func main() {
    app := &cli.App{
        Flags: clibind.FlagsFromStruct(Config{}),
        Action: func(ctx context.Context, cmd *cli.Command) error {
            var cfg Config
            if err := clibind.Bind(cmd, &cfg); err != nil {
                return err
            }
            log.Printf("config: %+v", cfg)
            return nil
        },
    }
    if err := app.Run(context.Background(), []string{"demo"}); err != nil {
        log.Fatal(err)
    }
}

Even quickier start

import (
    "context"
    "fmt"
    "os"

    "github.com/urfave/cli/v3"
    clibind "github.com/eosproject/urfave-cli-bind"
)

type ServerCfg struct {
    Addr string `cli:"addr"   cliDefault:"127.0.0.1:8080"`
    TLS  bool   `cli:"tls"`
}

func main() {
    app := clibind.CommandWithBinding(nil, "app", 
        func(ctx context.Context, cfg ServerCfg) error {
            fmt.Printf("serving %s tls=%v\n", cfg.Addr, cfg.TLS)
            return nil
        },
    )

    _ = app.Run(context.Background(), os.Args)
}

Tag reference

Tag Purpose
cli:"name,alias,alias2" Primary flag name plus optional aliases; add ,omitempty to skip unset optional flags.
cliDefault:"value" Default value shown in help and used when the flag is missing.
cliUsage:"text" Usage/help text surfaced in urfave/cli output.
cliPrefix:"foo." Applied to every nested field when recursing into a struct field.
cliTimeLayout:"2006-01-02" Overrides the RFC3339 default for time.Time parsing.

Nested structs and prefixes

  • Anonymous embedded structs without cliPrefix are flattened so their fields become top-level flags.
  • Named struct fields can opt-in to namespacing by providing cliPrefix. The prefix is prepended to all generated flag names and multi-character aliases, mirroring how Bind searches for values.

Binding rules

  • Bind requires a non-nil pointer to a struct and mirrors the type handling used in flag generation.
  • Required flags are inferred: if a field omits omitempty and lacks cliDefault, the generated flag is marked as required.
  • Slices use comma-separated defaults (cliDefault:"a,b,c"), duration fields expect the Go duration syntax, and UUID fields are treated as strings and parsed inside Bind.

Documentation

Overview

Package clibind provides a tag/reflect-based mapper between urfave/cli/v3 flags and a struct. Usage:

type Config struct {
    Name   string        `cli:"name,n,A"  cliDefault:"guest" cliUsage:"User name"`
    Count  int           `cli:"count,c"   cliDefault:"3"     cliUsage:"How many items"`
    Delay  time.Duration `cli:"delay,d"   cliDefault:"250ms" cliUsage:"Wait duration"`
    When   time.Time     `cli:"when,w"    cliDefault:"2025-01-02T15:04:05Z" cliUsage:"When (RFC3339)"`
    IDs    []uuid.UUID   `cli:"ids"       cliDefault:"b33a...-0001, b33a...-0002" cliUsage:"Comma-separated UUIDs"`
    Tags   []string      `cli:"tags"      cliDefault:"alpha,beta" cliUsage:"Comma-separated"`
}

app := &cli.App{
    Flags: clibind.FlagsFromStruct(Config{}),
    Action: func(ctx context.Context, c *cli.Command) error {
        var cfg Config
        if err := clibind.Bind(c, &cfg); err != nil { return err }
        // use cfg...
        return nil
    },
}

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Bind

func Bind(ctx *cli.Command, dest any) error

Bind populates struct fields from CLI flag values defined in the given command context. It expects dest to be a pointer to a struct whose fields are tagged or named to correspond to the command’s flags.

dest must be a non-nil pointer to a struct, otherwise Bind returns an error.

func CommandWithBinding

func CommandWithBinding[T any](
	base *cli.Command,
	name string,
	fn func(ctx context.Context, t T) error,
) *cli.Command

CommandWithBinding creates a new CLI command that automatically binds command-line flags into a typed configuration struct before executing the provided handler function.

It combines command construction and type-safe binding in one step.

If base is nil, a new *cli.Command is created. The resulting command’s Action is set using WithBinding(fn), and its Name is set to the provided name.

Example:

func runServer(ctx context.Context, cfg ServerConfig) error { ... }

root := &cli.Command{Name: "root"}
server := clibind.CommandWithBinding(root, "serve", runServer)

When executed, the "serve" subcommand will parse CLI flags, populate a ServerConfig instance via Bind, and then invoke runServer with the bound configuration.

func FlagsFromStruct

func FlagsFromStruct(v any) []cli.Flag

FlagsFromStruct inspects exported fields with `cli` and other tags and generates cli.Flag definitions. It is safe to pass either a struct or a pointer to a struct. Unexported fields are ignored.

func WithBinding

func WithBinding[T any](
	fn func(ctx context.Context, t T) error,
) func(ctx context.Context, c *cli.Command) (err error)

WithBinding wraps a typed handler function so that it automatically binds CLI flag values to a struct before invoking the handler.

The generic parameter T defines the type of the struct into which CLI flags will be bound. The provided function fn receives a populated instance of T.

This allows you to write clean, strongly typed handlers without manually parsing or binding CLI flags.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL