Socket
Socket
Sign inDemoInstall

github.com/jurgen-kluft/writ

Package Overview
Dependencies
0
Alerts
File Explorer

Install Socket

Detect and block malicious and high-risk dependencies

Install

    github.com/jurgen-kluft/writ

Writ is a flexible option parser with thorough test coverage. It's meant to be simple and "just work". Applications using writ look and behave similar to common GNU command-line applications, making them comfortable for end-users. Writ implements option decoding with GNU getopt_long conventions. All long and short-form option variations are supported: --with-x, --name Sam, --day=Friday, -i FILE, -vvv, etc. Help output generation is supported using text/template. The default template can be overriden with a custom template. Writ uses the Command and Option types to represent available options and subcommands. Input arguments are decoded with Command.Decode(). For convenience, the New() function can parse an input struct into a Command with Options that represent the input struct's fields. It uses struct field tags to control the behavior. The resulting Command's Decode() method updates the struct's fields in-place when option arguments are decoded. Alternatively, Commands and Options may be created directly. All fields on these types are exported. Options are specified via the "option" and "flag" struct tags. Both represent options, but fields marked "option" take arguments, whereas fields marked "flag" do not. Every Option must have an OptionDecoder. Writ provides decoders for most basic types, as well as some convenience types. See the NewOptionDecoder() function docs for details. New() parses an input struct to build a top-level Command. Subcommands are supported by using the "command" field tag. Fields marked with "command" must be of struct type, and are parsed the same way as top-level commands. Writ provides methods for generating help output. Command.WriteHelp() generates help content and writes to a given io.Writer. Command.ExitHelp() writes help content to stdout or stderr and terminates the program. Writ uses a template to generate the help content. The default template mimics --help output for common GNU programs. See the documentation of the Help type for more details. The New() function recognizes the following combinations of field tags: If both "default" and "env" are specified for an option field, the environment variable is consulted first. If the environment variable is present and decodes without error, that value is used. Otherwise, the value for the "default" tag is used. Values specified via parsed arguments take precedence over both types of defaults. This example uses writ.New() to build a command from the Greeter's struct fields. The resulting *writ.Command decodes and updates the Greeter's fields in-place. The Command.ExitHelp() method is used to display help content if --help is specified, or if invalid input arguments are received. This example demonstrates some of the convenience features offered by writ. It uses writ's support for io types and default values to ensure the Input and Output fields are initialized. These default to stdin and stdout due to the default:"-" field tags. The user may specify -i or -o to read from or write to a file. Similarly, the Replacements map is initialized with key=value pairs for every -r/--replace option the user specifies. This example demonstrates explicit Command and Option creation, along with explicit option grouping. It checks the host platform and dynamically adds a --bootloader option if the example is run on Linux. The same result could be achieved by using writ.New() to construct a Command, and then adding the platform-specific option to the resulting Command directly. This example demonstrates subcommands in a busybox style. There's no requirement that subcommands implement the Run() method shown here. It's just an example of how subcommands might be implemented.


Version published

Readme

Source

Build Status Coverage Report Card GoDoc

Writ

Overview

Writ is a flexible option parser with thorough test coverage. It's meant to be simple and "just work". Applications using writ look and behave similar to common GNU command-line applications, making them comfortable for end-users.

Writ implements option decoding with GNU getopt_long conventions. All long and short-form option variations are supported: --with-x, --name Sam, --day=Friday, -i FILE, -vvv, etc.

Help output generation is supported using text/template. The default template can be overriden with a custom template.

API Promise

Minor breaking changes may occur prior to the 1.0 release. After the 1.0 release, the API is guaranteed to remain backwards compatible.

Basic Use

Please see the godocs for additional information.

This example uses writ.New() to build a command from the Greeter's struct fields. The resulting *writ.Command decodes and updates the Greeter's fields in-place. The Command.ExitHelp() method is used to display help content if --help is specified, or if invalid input arguments are received.

Source:

package main

import (
    "fmt"
    "github.com/bobziuchkovski/writ"
    "strings"
)

type Greeter struct {
    HelpFlag  bool   `flag:"help" description:"display this help message"`
    Verbosity int    `flag:"v, verbose" description:"display verbose output"`
    Name      string `option:"n, name" default:"Everyone" description:"the person to greet"`
}

func main() {
    greeter := &Greeter{}
    cmd := writ.New("greeter", greeter)
    cmd.Help.Usage = "Usage: greeter [OPTION]... MESSAGE"
    cmd.Help.Header = "Greet users, displaying MESSAGE"

    // Use cmd.Decode(os.Args[1:]) in a real application
    _, positional, err := cmd.Decode([]string{"-vvv", "--name", "Sam", "How's it going?"})
    if err != nil || greeter.HelpFlag {
        cmd.ExitHelp(err)
    }

    message := strings.Join(positional, " ")
    fmt.Printf("Hi %s! %s\n", greeter.Name, message)
    if greeter.Verbosity > 0 {
        fmt.Printf("I'm feeling re%slly chatty today!\n", strings.Repeat("a", greeter.Verbosity))
    }

    // Output:
    // Hi Sam! How's it going?
    // I'm feeling reaaally chatty today!
}

Help output:

Usage: greeter [OPTION]... MESSAGE
Greet users, displaying MESSAGE

Available Options:
  --help                    Display this help message and exit
  -v, --verbose             Display verbose output
  -n, --name=ARG            The person or people to greet

Subcommands

Please see the godocs for additional information.

This example demonstrates subcommands in a busybox style. There's no requirement that subcommands implement the Run() method shown here. It's just an example of how subcommands might be implemented.

Source:

package main

import (
    "errors"
    "github.com/bobziuchkovski/writ"
    "os"
)

type GoBox struct {
    Link Link `command:"ln" alias:"link" description:"Create a soft or hard link"`
    List List `command:"ls" alias:"list" description:"List directory contents"`
}

type Link struct {
    HelpFlag bool `flag:"h, help" description:"Display this message and exit"`
    Symlink  bool `flag:"s" description:"Create a symlink instead of a hard link"`
}

type List struct {
    HelpFlag   bool `flag:"h, help" description:"Display this message and exit"`
    LongFormat bool `flag:"l" description:"Use long-format output"`
}

func (r *GoBox) Run(p writ.Path, positional []string) {
    // The user didn't specify a subcommand.  Give them help.
    p.Last().ExitHelp(errors.New("COMMAND is required"))
}

func (r *Link) Run(p writ.Path, positional []string) {
    if r.HelpFlag {
        p.Last().ExitHelp(nil)
    }
    if len(positional) != 2 {
        p.Last().ExitHelp(errors.New("ln requires two arguments, OLD and NEW"))
    }
    // Link operation omitted for brevity.  This would be os.Link or os.Symlink
    // based on the r.Symlink value.
}

func (r *List) Run(p writ.Path, positional []string) {
    if r.HelpFlag {
        p.Last().ExitHelp(nil)
    }
    // Listing operation omitted for brevity.  This would be a call to ioutil.ReadDir
    // followed by conditional formatting based on the r.LongFormat value.
}

func main() {
    gobox := &GoBox{}
    cmd := writ.New("gobox", gobox)
    cmd.Help.Usage = "Usage: gobox COMMAND [OPTION]... [ARG]..."
    cmd.Subcommand("ln").Help.Usage = "Usage: gobox ln [-s] OLD NEW"
    cmd.Subcommand("ls").Help.Usage = "Usage: gobox ls [-l] [PATH]..."

    path, positional, err := cmd.Decode(os.Args[1:])
    if err != nil {
        // Using path.Last() here ensures the user sees relevant help for their
        // command selection
        path.Last().ExitHelp(err)
    }

    // At this point, cmd.Decode() has already decoded option values into the gobox
    // struct, including subcommand values.  We just need to dispatch the command.
    // path.String() is guaranteed to represent the user command selection.
    switch path.String() {
    case "gobox":
        gobox.Run(path, positional)
    case "gobox ln":
        gobox.Link.Run(path, positional)
    case "gobox ls":
        gobox.List.Run(path, positional)
    default:
        panic("BUG: Someone added a new command and forgot to add it's path here")
    }
}

Help output, gobox:

Usage: gobox COMMAND [OPTION]... [ARG]...

Available Commands:
  ln                        Create a soft or hard link
  ls                        List directory contents

Help output, gobox ln:

Usage: gobox ln [-s] OLD NEW

Available Options:
  -h, --help                Display this message and exit
  -s                        Create a symlink instead of a hard link

Help output, gobox ls:

Usage: gobox ls [-l] [PATH]...

Available Options:
  -h, --help                Display this message and exit
  -l                        Use long-format output

Explicit Commands and Options

Please see the godocs for additional information.

This example demonstrates explicit Command and Option creation, along with explicit option grouping. It checks the host platform and dynamically adds a --bootloader option if the example is run on Linux. The same result could be achieved by using writ.New() to construct a Command, and then adding the platform-specific option to the resulting Command directly.

Source:

package main

import (
    "github.com/bobziuchkovski/writ"
    "os"
    "runtime"
)

type Config struct {
    help       bool
    verbosity  int
    bootloader string
}

func main() {
    config := &Config{}
    cmd := &writ.Command{Name: "explicit"}
    cmd.Help.Usage = "Usage: explicit [OPTION]... [ARG]..."
    cmd.Options = []*writ.Option{
        {
            Names:       []string{"h", "help"},
            Description: "Display this help text and exit",
            Decoder:     writ.NewFlagDecoder(&config.help),
            Flag:        true,
        },
        {
            Names:       []string{"v"},
            Description: "Increase verbosity; may be specified more than once",
            Decoder:     writ.NewFlagAccumulator(&config.verbosity),
            Flag:        true,
            Plural:      true,
        },
    }

    general := cmd.GroupOptions("help", "v")
    general.Header = "General Options:"
    cmd.Help.OptionGroups = append(cmd.Help.OptionGroups, general)

    // Dynamically add --bootloader on Linux
    if runtime.GOOS == "linux" {
        cmd.Options = append(cmd.Options, &writ.Option{
            Names:       []string{"bootloader"},
            Description: "Use the specified bootloader (grub, grub2, or lilo)",
            Decoder:     writ.NewOptionDecoder(&config.bootloader),
            Placeholder: "NAME",
        })
        platform := cmd.GroupOptions("bootloader")
        platform.Header = "Platform Options:"
        cmd.Help.OptionGroups = append(cmd.Help.OptionGroups, platform)
    }

    // Decode the options
    _, _, err := cmd.Decode(os.Args[1:])
    if err != nil || config.help {
        cmd.ExitHelp(err)
    }
}

Help output, Linux:

General Options:
  -h, --help                Display this help text and exit
  -v                        Increase verbosity; may be specified more than once

Platform Options:
  --bootloader=NAME         Use the specified bootloader (grub, grub2, or lilo)

Help output, other platforms:

General Options:
  -h, --help                Display this help text and exit
  -v                        Increase verbosity; may be specified more than once

Authors

Bob Ziuchkovski (@bobziuchkovski)

License (MIT)

Copyright (c) 2016 Bob Ziuchkovski

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

FAQs

Last updated on 04 Feb 2016

Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc