getoptlong

package module
v0.0.0-...-86577a8 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: 3 Imported by: 0

README

go-ruby-getoptlong/getoptlong

getoptlong — go-ruby-getoptlong

Docs License Go Coverage

A pure-Go (no cgo) reimplementation of Ruby's GetoptLong — the getopt-style command-line option parser bundled with MRI (ruby/getoptlong 0.2.1, the version shipped with Ruby 4.0.5). It scans an argument list for options exactly as GetoptLong does — the same long/short/abbreviation matching, the same =-joined and separate arguments, the same bundled short flags, the same -- terminator, the same three ordering modes, and the same error taxonomy with MRI-identical messageswithout any Ruby runtime.

It is the GetoptLong backend for go-embedded-ruby, but is a standalone, reusable module with no dependency on the Ruby runtime — a sibling of go-ruby-yaml (the Psych port) and go-ruby-regexp (the Onigmo engine).

What it is — and isn't. Argv scanning is pure compute and fully deterministic, so it lives here as pure Go. Unlike Ruby's GetoptLong, which mutates the global ARGV and reads $0 / POSIXLY_CORRECT / $stderr, this package operates on an explicit, Parser-owned argument slice and writes errors to a caller-supplied io.Writer — so it is reusable and free of global state. The host (for example rbgo) binds ARGV and $0 to a Parser and reads the remaining, non-option arguments back from Parser.Args after processing.

Features

Faithful port of GetoptLong#get / #each, validated against the ruby binary on every supported platform:

  • Long, short, and abbreviated option names. A long name may be given by any unique prefix; an ambiguous prefix raises AmbiguousOption.
  • Aliases — an option may have any number of names; the parsed option always reports the canonical (first) name, not the alias used.
  • Argument forms--name=value and --name value for long options; -nvalue, -n value, and bundled -abc for short options.
  • Three argument flagsNoArgument, RequiredArgument, OptionalArgument — with MRI's exact rules for when the next word is consumed.
  • Three ordering modesPermute (the default; options and operands mix freely), RequireOrder (options must precede operands), and ReturnInOrder (operands are returned as ("", word) options).
  • The -- terminator ends option processing; subsequent words are operands.
  • Full error taxonomyInvalidOption, MissingArgument, NeedlessArgument, AmbiguousOption (all under Error) — with the exact POSIX-format messages MRI produces, optional $0-style program-name prefix, and a quiet mode.

CGO-free, dependency-free, 100% test coverage, gofmt + go vet clean, and green across the six 64-bit Go targets (amd64, arm64, riscv64, loong64, ppc64le, s390x) and three operating systems (Linux, macOS, Windows).

Install

go get github.com/go-ruby-getoptlong/getoptlong

Usage

package main

import (
	"fmt"
	"os"

	"github.com/go-ruby-getoptlong/getoptlong"
)

func main() {
	p, err := getoptlong.New(os.Args[1:],
		getoptlong.Option{Names: []string{"--number", "-n"}, Flag: getoptlong.RequiredArgument},
		getoptlong.Option{Names: []string{"--verbose", "-v"}, Flag: getoptlong.OptionalArgument},
		getoptlong.Option{Names: []string{"--help", "-h"}, Flag: getoptlong.NoArgument},
	)
	if err != nil {
		panic(err) // a *SpecError: a malformed option specification
	}
	p.ProgName = "fib"        // mirrors Ruby's $0 in error messages
	p.ErrorWriter = os.Stderr // mirrors Ruby's $stderr

	err = p.Each(func(name, argument string) {
		fmt.Printf("%-10s %q\n", name, argument)
	})
	if err != nil {
		// err is a *getoptlong.Error: InvalidOption / MissingArgument / ...
		os.Exit(1)
	}
	fmt.Println("operands:", p.Args) // the remaining, non-option arguments
}
$ fib --number 6 -v -- extra
--number   "6"
--verbose  ""
operands: [extra]

Ordering

The three ordering modes mirror Ruby's GetoptLong exactly (set with SetOrdering). Given Foo --zzz Bar --xxx Baz Bat with --xxx taking a required argument and --zzz taking none:

Ordering Options yielded p.Args after
Permute ("--zzz",""), ("--xxx","Baz") [Foo Bat]
RequireOrder (none — first word is an operand) [Foo --zzz Bar --xxx Baz Bat]
ReturnInOrder ("","Foo"), ("--zzz",""), ("--xxx","Baz"), ("","Bat") []

Unlike Ruby, this package does not consult the POSIXLY_CORRECT environment variable; pass RequireOrder explicitly for POSIX behaviour.

API

// New configures a Parser to scan args for the given options (PERMUTE ordering).
func New(args []string, options ...Option) (*Parser, error)
func NewParser() *Parser

type ArgumentFlag int
const (NoArgument; RequiredArgument; OptionalArgument)

type Ordering int
const (RequireOrder; Permute; ReturnInOrder)

type Option struct { Names []string; Flag ArgumentFlag }

type Parser struct {
	Args        []string  // working argv; leftover operands after scanning
	ProgName    string    // error-message prefix (Ruby's $0)
	ErrorWriter io.Writer // error sink (Ruby's $stderr); nil discards
	// ...
}

func (p *Parser) GetNext() (name, argument string, ok bool, err error) // GetoptLong#get
func (p *Parser) Each(fn func(name, argument string)) error            // GetoptLong#each
func (p *Parser) SetOptions(options ...Option) error                  // set_options
func (p *Parser) SetOrdering(o Ordering) error                        // ordering=
func (p *Parser) Ordering() Ordering
func (p *Parser) SetQuiet(q bool); func (p *Parser) Quiet() bool      // quiet=
func (p *Parser) Err() *Error; func (p *Parser) ErrorMessage() string
func (p *Parser) Terminated() bool

type Error struct { Kind ErrorKind; Message string }      // GetoptLong::Error
type ErrorKind int                                        // InvalidOption / MissingArgument /
                                                          // NeedlessArgument / AmbiguousOption
type SpecError struct { Message string }                  // ArgumentError from New / SetOptions

GetNext returns ("", "", false, nil) when there are no more options; when ordering is ReturnInOrder an operand is returned as ("", word, true, nil). On a parse error it returns ("", "", false, *Error) and records the error (Err / ErrorMessage); subsequent calls yield nothing.

Tests & coverage

The suite pairs deterministic, ruby-free golden tests (which alone hold coverage at 100%, so the qemu cross-arch and Windows lanes pass the gate) with a differential MRI oracle: every scenario — all three orderings, long / short / abbreviated names, =-joined / separate / bundled arguments, the -- terminator, and each error class — is run through both this package and the system ruby and compared byte-for-byte (option stream, leftover argv, error class + message). The oracle scripts $stdout.binmode / $stdin.binmode so Windows text-mode never pollutes the bytes, are gated on RUBY_VERSION >= "4.0", and skip themselves where ruby is absent.

COVERPKG=$(go list ./... | paste -sd, -)
go test -race -coverpkg="$COVERPKG" -coverprofile=cover.out ./...
go tool cover -func=cover.out | tail -1   # 100.0%

License

BSD-3-Clause — see LICENSE. Copyright the go-ruby-getoptlong/getoptlong authors.

WebAssembly

Being pure Go (CGO=0), this library also compiles to WebAssembly — both GOOS=js GOARCH=wasm (browser / Node.js) and GOOS=wasip1 GOARCH=wasm (WASI). CI builds both targets on every push, alongside the six 64-bit native/qemu arches.

GOOS=js     GOARCH=wasm go build ./...   # browser / Node
GOOS=wasip1 GOARCH=wasm go build ./...   # WASI (wasmtime, wasmer, wasmedge, …)

Documentation

Overview

Package getoptlong is a pure-Go (no cgo) reimplementation of Ruby's GetoptLong — the getopt-style command-line option parser shipped with MRI (ruby/getoptlong 0.2.1, the version bundled with Ruby 4.0.5).

It is a faithful, byte-for-byte port of MRI's option-scanning algorithm: the same long/short/abbreviation matching, the same =-joined and separate arguments, the same bundled short flags, the same `--` terminator, the same three ordering modes (REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER) and the same error taxonomy (InvalidOption, MissingArgument, NeedlessArgument, AmbiguousOption) with MRI-identical messages.

Unlike Ruby's GetoptLong, which mutates the global ARGV, this package operates on an explicit argument slice owned by the Parser, so it is reusable and free of global state. The host (for example go-embedded-ruby's `rbgo`) binds ARGV to a Parser and reads the remaining, non-option arguments back from Parser.Args after processing.

The scanning is pure compute and needs no Ruby runtime; the package has no dependencies.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ArgumentFlag

type ArgumentFlag int

ArgumentFlag describes whether an option takes an argument. The values match Ruby's GetoptLong::NO_ARGUMENT / REQUIRED_ARGUMENT / OPTIONAL_ARGUMENT.

const (
	// NoArgument is GetoptLong::NO_ARGUMENT — the option takes no argument.
	NoArgument ArgumentFlag = 0
	// RequiredArgument is GetoptLong::REQUIRED_ARGUMENT — the option must be
	// followed by an argument.
	RequiredArgument ArgumentFlag = 1
	// OptionalArgument is GetoptLong::OPTIONAL_ARGUMENT — the option may be
	// followed by an argument.
	OptionalArgument ArgumentFlag = 2
)

type Error

type Error struct {
	// Kind is the concrete error class (InvalidOption, MissingArgument,
	// NeedlessArgument, or AmbiguousOption).
	Kind ErrorKind
	// Message is the POSIX-format message, identical to Ruby's
	// GetoptLong#error_message.
	Message string
}

Error is the base of the GetoptLong error taxonomy; it corresponds to GetoptLong::Error (a StandardError in Ruby). Every parsing error returned by GetNext wraps one of the four concrete kinds and reports an MRI-identical message via Error.Error.

func (*Error) Error

func (e *Error) Error() string

type ErrorKind

type ErrorKind int

ErrorKind enumerates the concrete GetoptLong error classes.

const (
	// InvalidOption is GetoptLong::InvalidOption — an unrecognised option.
	InvalidOption ErrorKind = iota
	// MissingArgument is GetoptLong::MissingArgument — a required argument is
	// absent.
	MissingArgument
	// NeedlessArgument is GetoptLong::NeedlessArgument — an argument was given
	// to a NO_ARGUMENT option.
	NeedlessArgument
	// AmbiguousOption is GetoptLong::AmbiguousOption — an abbreviation matches
	// more than one option.
	AmbiguousOption
)

func (ErrorKind) String

func (k ErrorKind) String() string

String returns the Ruby class name of the error kind, e.g. "GetoptLong::InvalidOption".

type Option

type Option struct {
	// Names is the canonical name (first) followed by any aliases.
	Names []string
	// Flag is the argument flag for this option.
	Flag ArgumentFlag
}

Option defines a single option: a canonical name, its aliases, and the argument flag. It is the Go form of one array passed to Ruby's GetoptLong.new, e.g. ["--name", "-n", GetoptLong::REQUIRED_ARGUMENT].

Names is the list of name and aliases; the first element is the canonical name reported by GetNext. Each name must be either "-" followed by a single non-hyphen character, or "--" followed by one or more characters.

type Ordering

type Ordering int

Ordering selects how options and non-option arguments are interpreted. The values match Ruby's GetoptLong::REQUIRE_ORDER / PERMUTE / RETURN_IN_ORDER.

const (
	// RequireOrder stops option processing at the first non-option word; every
	// word after it is a non-option word (GetoptLong::REQUIRE_ORDER).
	RequireOrder Ordering = 0
	// Permute lets options and non-option arguments mix in any order; the
	// non-option words are collected and returned at the end (GetoptLong::PERMUTE).
	// This is the default for a new Parser.
	Permute Ordering = 1
	// ReturnInOrder treats every word as an option: a non-option word is
	// returned as an option whose name is "" and whose value is the word
	// (GetoptLong::RETURN_IN_ORDER).
	ReturnInOrder Ordering = 2
)

type Parser

type Parser struct {
	// Args is the working argument slice. GetNext consumes options from the
	// front; on termination the collected non-option arguments are restored
	// here, so after a full scan Args is exactly the remaining ARGV.
	Args []string

	// ProgName is prepended to error messages written to ErrorWriter, matching
	// Ruby's $0. If empty, no program-name prefix is written.
	ProgName string

	// ErrorWriter receives error messages unless Quiet is set. Ruby writes them
	// to $stderr; if ErrorWriter is nil, messages are discarded (but GetNext
	// still returns the error). Set it to os.Stderr to mirror Ruby exactly.
	ErrorWriter io.Writer
	// contains filtered or unexported fields
}

Parser scans an argument slice for options. It is the Go counterpart of a GetoptLong instance. Configure it with New (or NewParser + SetOptions), set the ordering and quiet mode if needed, then call GetNext repeatedly. After processing, Args holds the remaining non-option arguments (the equivalent of the ARGV that Ruby leaves behind).

func New

func New(args []string, options ...Option) (*Parser, error)

New returns a Parser configured to scan args for the given options, in the default PERMUTE ordering. It is the counterpart of GetoptLong.new. A bad option specification returns a *SpecError.

func NewParser

func NewParser() *Parser

NewParser returns an empty Parser in the default PERMUTE ordering with no options configured. Add options with SetOptions. The argument slice defaults to nil; assign Parser.Args before scanning.

func (*Parser) Each

func (p *Parser) Each(fn func(name, argument string)) error

Each calls fn with each successive option until processing ends. It corresponds to GetoptLong#each / #each_option. It stops and returns the error if GetNext reports one.

func (*Parser) Err

func (p *Parser) Err() *Error

Err returns the error that terminated processing, or nil if none. It corresponds to GetoptLong#error.

func (*Parser) ErrorMessage

func (p *Parser) ErrorMessage() string

ErrorMessage returns the message of the error that terminated processing, or "" if none. It corresponds to GetoptLong#error_message.

func (*Parser) GetNext

func (p *Parser) GetNext() (name, argument string, ok bool, err error)

GetNext returns the next option as (name, argument, ok). name is the canonical option name (never an alias); argument is its value ("" when the option takes no argument or an optional argument was absent). ok is false when there are no more options — either the input is exhausted or an error occurred; check err for the latter. GetNext corresponds to GetoptLong#get / #get_option.

On error, GetNext returns ("", "", false) together with a non-nil *Error and records it (see Err / ErrorMessage).

func (*Parser) Ordering

func (p *Parser) Ordering() Ordering

Ordering returns the current ordering setting.

func (*Parser) Quiet

func (p *Parser) Quiet() bool

Quiet reports whether error messages are suppressed.

func (*Parser) SetOptions

func (p *Parser) SetOptions(options ...Option) error

SetOptions replaces the configured options. It corresponds to GetoptLong#set_options. It fails once processing has started, or if any specification is invalid (returning a *SpecError with an MRI-identical message). On any spec error the option tables are left empty, as in Ruby.

func (*Parser) SetOrdering

func (p *Parser) SetOrdering(o Ordering) error

SetOrdering sets the ordering. It corresponds to GetoptLong#ordering=. It fails once option processing has started. Unlike Ruby, this package does not consult the POSIXLY_CORRECT environment variable; pass RequireOrder explicitly for POSIX behaviour.

func (*Parser) SetQuiet

func (p *Parser) SetQuiet(q bool)

SetQuiet sets quiet mode; when true, error messages are not written to ErrorWriter (GetNext still returns the error). It corresponds to GetoptLong#quiet=.

func (*Parser) Terminated

func (p *Parser) Terminated() bool

Terminated reports whether option processing has finished. It corresponds to GetoptLong#terminated?.

type SpecError

type SpecError struct {
	Message string
}

SpecError reports an invalid option specification passed to New or SetOptions; it corresponds to the ArgumentError that Ruby's GetoptLong.new / #set_options raise. Its message is MRI-identical.

func (*SpecError) Error

func (e *SpecError) Error() string

Jump to

Keyboard shortcuts

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