thor

package module
v0.0.0-...-30c0ea4 Latest Latest
Warning

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

Go to latest
Published: Jul 17, 2026 License: BSD-3-Clause Imports: 5 Imported by: 0

Documentation

Overview

Package thor is a pure-Go (CGO-free) MRI-faithful reimplementation of the deterministic core of Ruby's Thor CLI-framework gem (thor 1.5.0). It covers the three interpreter-independent halves of Thor a host embeds: declarative option/argument parsing (Options, [Arguments]), the command registry and argv-to-command dispatch (Base), and byte-faithful usage/help generation.

Running a command body invokes user Ruby (or Go) code; that is the host seam. This package resolves argv into (command, parsed-options, remaining-args) and renders help exactly as the gem does, so go-embedded-ruby can bind Thor with no Ruby runtime and reproduce the gem's output and error text byte-for-byte.

Value model

Parsed option values use a small, fixed set of Go types so a host can map its object graph to and from this package. The mapping mirrors the one Thor's parser produces:

Ruby (Thor)      Go
-----------      --
nil              nil
true / false     bool
:string value    string
:numeric int     int64
:numeric float   float64
:array           []string
:hash            *OrderedMap (string->string, insertion order)

A parse yields a *Result: its Options is an insertion-ordered map from the option human name to its value, and Args holds the non-option remainder.

Terminal width

Help/table/wrap layout depends on terminal width. For deterministic, ruby-free golden output this package uses a fixed default of 80 columns, overridable per call via Config.TerminalWidth or the THOR_COLUMNS environment variable, matching Thor::Shell::Terminal.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Split

func Split(args []string) (arguments, rest []string)

Split partitions argv into leading positional arguments and the switch tail, the way Thor::Arguments.split does: stop at the first token starting with "-".

Types

type Base

type Base struct {
	// Namespace is the class namespace used in usage prefixes (e.g. "myapp").
	Namespace string
	// ClassOptions are options shared by every command (Thor `class_option`),
	// in declaration order.
	ClassOptions []*Option
	// Arguments are the class's declared positional arguments, in order.
	Arguments []*Option
	// DefaultCommand is invoked when argv names none; "" means "help".
	DefaultCommand string
	// Map holds `map` aliases (input token -> command name).
	Map map[string]string
	// contains filtered or unexported fields
}

Base is a command registry: the Go analogue of a Thor subclass. It resolves an argv to a command, parses that command's options, and renders help — all deterministically, with command-body invocation left to the host.

func NewBase

func NewBase(namespace string, config Config) *Base

NewBase returns an empty registry with the given namespace and config.

func (*Base) AddCommand

func (b *Base) AddCommand(c *Command)

AddCommand registers a command (normalizing foo-bar to foo_bar as the key).

func (*Base) CommandHelp

func (b *Base) CommandHelp(commandName string) (string, error)

CommandHelp renders the per-command help block (Thor::command_help) for the named command, byte-faithful to the gem, or a KindUndefinedCommand error.

func (*Base) Commands

func (b *Base) Commands() []*Command

Commands returns the registered commands in registration order.

func (*Base) Dispatch

func (b *Base) Dispatch(argv []string) (*Command, *Result, error)

Dispatch resolves argv into a command, its parsed options, and the remaining positional arguments. It mirrors the deterministic half of Thor::dispatch: pick the command name, split argv at the first switch, and parse options. Invoking the resolved command body is the host seam.

func (*Base) Help

func (b *Base) Help() string

Help renders the class-level help (Thor::help): the command listing followed by the class options table, byte-faithful to the gem.

func (*Base) NormalizeCommandName

func (b *Base) NormalizeCommandName(meth string) (string, error)

NormalizeCommandName resolves a user token to a command name the way Thor::normalize_command_name does: nil -> default; prefix completion; map aliases; ambiguity is an error. The final name uses underscores.

type Command

type Command struct {
	// Name is the command's normalized method name (underscores).
	Name string
	// Description is the one-line `desc` text.
	Description string
	// LongDescription is the `long_desc` text ("" if none).
	LongDescription string
	// WrapLongDescription toggles word-wrapping of LongDescription (long_desc
	// wrap: option; default true).
	WrapLongDescription bool
	// Usage is the raw usage line (`desc "usage", ...`).
	Usage string
	// Options are the command's options in declaration order.
	Options []*Option
	// AncestorName, when set, prefixes the usage (subcommand ancestor).
	AncestorName string
	// Hidden hides the command from help listings.
	Hidden bool
	// Relations holds this command's exclusive / at-least-one option groups.
	Relations Relations
}

Command models a registered command (Thor::Command): its name, description, usage line, and the options declared for it. Invoking the command body is the host seam; this type carries only the metadata used for dispatch and help.

func NewCommand

func NewCommand(name, description, usage string, options []*Option) *Command

NewCommand builds a Command with WrapLongDescription defaulting to true.

func (*Command) FormattedUsage

func (c *Command) FormattedUsage(namespace string, args []*Option, useNamespace, subcommand bool) string

FormattedUsage renders the command's usage line as Thor::Command#formatted_usage does: an ancestor / namespace / subcommand prefix, the usage with required class arguments injected, then required options. namespace is the class's namespace; args are the class's declared positional arguments (may be nil).

type Config

type Config struct {
	// Basename is the program name shown at the head of usage/banner lines
	// (Thor's basename, e.g. "myapp"). Defaults to "thor" when empty.
	Basename string
	// TerminalWidth overrides the layout width; 0 uses THOR_COLUMNS then 80.
	TerminalWidth int
	// PackageName, when set, heads the command listing with
	// "<PackageName> commands:" instead of "Commands:".
	PackageName string
	// Getenv resolves environment variables (for THOR_COLUMNS); nil means the
	// process environment is not consulted and only TerminalWidth/80 apply.
	Getenv func(string) string
}

Config tunes dispatch and help rendering.

type Error

type Error struct {
	Kind ErrorKind
	Msg  string
}

Error is a Thor parse or dispatch error. Msg is byte-identical to the message the gem raises, and Kind names the gem exception class so the host can re-raise it faithfully.

func (*Error) Error

func (e *Error) Error() string

Error implements the error interface, returning the gem-exact message.

type ErrorKind

type ErrorKind int

ErrorKind classifies a Thor parse/dispatch error by the gem exception class it corresponds to, so a host can re-raise the matching Ruby exception.

const (
	// KindRequiredArgumentMissing maps to Thor::RequiredArgumentMissingError.
	KindRequiredArgumentMissing ErrorKind = iota
	// KindMalformattedArgument maps to Thor::MalformattedArgumentError.
	KindMalformattedArgument
	// KindUnknownArgument maps to Thor::UnknownArgumentError.
	KindUnknownArgument
	// KindExclusiveArgument maps to Thor::ExclusiveArgumentError.
	KindExclusiveArgument
	// KindAtLeastOneRequiredArgument maps to Thor::AtLeastOneRequiredArgumentError.
	KindAtLeastOneRequiredArgument
	// KindUndefinedCommand maps to Thor::UndefinedCommandError.
	KindUndefinedCommand
	// KindAmbiguousCommand maps to Thor::AmbiguousCommandError.
	KindAmbiguousCommand
	// KindArgument maps to Ruby's ArgumentError (invalid declaration).
	KindArgument
)

func (ErrorKind) String

func (k ErrorKind) String() string

String returns the fully-qualified gem exception class name for the kind.

type Option

type Option struct {
	// Name is the raw declared name (may use "_" or "-"). It drives SwitchName
	// and HumanName below.
	Name string
	// Desc is the one-line description shown in the option table.
	Desc string
	// Required marks the option as required (defaults to false for options).
	Required bool
	// Type is the value type; empty means String.
	Typ Type
	// Default is the default value (any of the mapped Go value types), or nil.
	Default any
	// LazyDefault is Thor's :lazy_default — the value used when the switch is
	// given without an inline value.
	LazyDefault any
	// Aliases are the short/long alias switches (e.g. "-f", "--flag"),
	// normalized to carry a leading dash.
	Aliases []string
	// Banner overrides the value placeholder shown in usage; empty uses the
	// per-type default banner.
	Banner string
	// BannerSet records whether Banner was explicitly provided (so an explicit
	// empty banner suppresses the placeholder).
	BannerSet bool
	// Enum, when non-empty, restricts accepted values.
	Enum []string
	// Group is the capitalized option group name ("" is the default group).
	Group string
	// Hide hides the option from help output.
	Hide bool
	// Repeatable accumulates repeated switches into an array (or merged hash).
	Repeatable bool
}

Option models a single declared option (Thor::Option), i.e. a switch given via `option`/`method_option`/`method_options`. The zero value is not valid; build one with NewOption.

func NewOption

func NewOption(name string, o Option) (*Option, error)

NewOption returns an Option with the Thor defaults applied: Type defaults to String and, when unset, the banner defaults per type. It normalizes aliases to carry a leading dash. It returns an error for the invalid declarations Thor rejects (boolean+required).

func (*Option) Boolean

func (o *Option) Boolean() bool

Boolean reports whether the option is of boolean type.

func (*Option) EnumToS

func (o *Option) EnumToS() string

EnumToS renders the enum for messages/help (Thor::Argument#enum_to_s).

func (*Option) HumanName

func (o *Option) HumanName() string

HumanName is the option's programmatic name (the key in the options hash).

func (*Option) PrintDefault

func (o *Option) PrintDefault() string

PrintDefault renders the default value as it appears after "# Default:".

func (*Option) ShowDefault

func (o *Option) ShowDefault() bool

ShowDefault reports whether a "# Default:" line is shown for this option.

func (*Option) StringType

func (o *Option) StringType() bool

StringType reports whether the option is of string type.

func (*Option) SwitchName

func (o *Option) SwitchName() string

SwitchName is the canonical switch (e.g. "--flag") used as the map key.

func (*Option) Usage

func (o *Option) Usage(padding int) string

Usage renders the option's line in the help table, padded so the switch columns align (Thor::Option#usage). padding is the max aliases-prefix width.

type Options

type Options struct {
	// contains filtered or unexported fields
}

Options declares a set of options to parse against. Build one with NewOptions; call Options.Parse to parse an argv.

func NewOptions

func NewOptions(options []*Option, defaults *ValueMap, stopOnUnknown, disableRequiredCheck bool, relations Relations) *Options

NewOptions builds an Options parser from declared options and a defaults map (option human name -> value; Thor's `defaults` seeded from class/method options). Pass a zero Relations for no exclusive/at-least-one groups.

func (*Options) Parse

func (o *Options) Parse(argv []string) (*Result, error)

Parse parses argv against the declared options and returns the result (parsed options + remaining args) or a gem-faithful *Error.

type OrderedMap

type OrderedMap struct {
	// contains filtered or unexported fields
}

OrderedMap is an insertion-ordered string->string map, the Go shape of a Thor :hash option value. Thor builds hash values by walking "key:value" tokens left to right, so insertion order is meaningful and preserved here.

func NewOrderedMap

func NewOrderedMap() *OrderedMap

NewOrderedMap returns an empty ordered map.

func (*OrderedMap) Get

func (m *OrderedMap) Get(key string) (string, bool)

Get returns the value for key and whether it was present.

func (*OrderedMap) Has

func (m *OrderedMap) Has(key string) bool

Has reports whether key is present.

func (*OrderedMap) Keys

func (m *OrderedMap) Keys() []string

Keys returns the keys in insertion order. The slice must not be mutated.

func (*OrderedMap) Len

func (m *OrderedMap) Len() int

Len reports the number of entries.

func (*OrderedMap) Merge

func (m *OrderedMap) Merge(other *OrderedMap)

Merge copies every entry of other into m in other's insertion order, mirroring how a repeatable :hash option accumulates via Hash#merge!.

func (*OrderedMap) Set

func (m *OrderedMap) Set(key, val string)

Set inserts or replaces key. A repeated key keeps its original position and overwrites the value, matching Ruby Hash#[]= semantics (though Thor's parser rejects duplicate keys before they reach here).

type Relations

type Relations struct {
	Exclusive  [][]string
	AtLeastOne [][]string
}

Relations groups options for the exclusive / at-least-one checks. Each entry is a slice of option human names.

type Result

type Result struct {
	// Options maps each option's human name to its parsed value (a string,
	// int64, float64, bool, []string, or *OrderedMap), in declaration order.
	Options *ValueMap
	// Args holds the arguments left over after option parsing (Thor's #remaining).
	Args []string
}

Result is the outcome of a successful parse: the parsed option values keyed by option human name in declaration order, and the non-option remainder.

type Type

type Type string

Type is a Thor option/argument value type (Thor::Option::VALID_TYPES).

const (
	String  Type = "string"
	Numeric Type = "numeric"
	Boolean Type = "boolean"
	Array   Type = "array"
	Hash    Type = "hash"
)

The Thor option value types.

type ValueMap

type ValueMap struct {
	// contains filtered or unexported fields
}

ValueMap is an insertion-ordered map from option human name to parsed value.

func NewValueMap

func NewValueMap() *ValueMap

NewValueMap returns an empty value map.

func (*ValueMap) Get

func (m *ValueMap) Get(key string) (any, bool)

Get returns the value for key and whether it was present.

func (*ValueMap) Has

func (m *ValueMap) Has(key string) bool

Has reports whether key is present.

func (*ValueMap) Keys

func (m *ValueMap) Keys() []string

Keys returns the keys in insertion order. The slice must not be mutated.

func (*ValueMap) Len

func (m *ValueMap) Len() int

Len reports the number of entries.

func (*ValueMap) Set

func (m *ValueMap) Set(key string, val any)

Set inserts or replaces key, preserving first-insertion position.

Jump to

Keyboard shortcuts

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