blob: 951fa55168f8a269b2b944c929c2ccbde52e43d7 [file] [log] [blame]
// Copyright 2017 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package subcmd // import "upspin.io/subcmd"
import (
"fmt"
"io"
"os"
"upspin.io/bind"
"upspin.io/client"
"upspin.io/shutdown"
"upspin.io/upspin"
)
// State describes the state of a subcommand.
// See the comments for Exitf to see how Interactive is used.
// It allows a program to run multiple commands.
type State struct {
Name string // Name of the subcommand we are running.
Config upspin.Config // Config; may be nil.
Client upspin.Client // Client; may be nil.
Interactive bool // Whether the command is line-by-line.
Stdin io.Reader // Where to read standard input
Stdout io.Writer // Where to write standard output.
Stderr io.Writer // Where to write error output.
ExitCode int // Exit with non-zero status for minor problems.
}
// NewState returns a new State for the named subcommand.
func NewState(name string) *State {
s := &State{Name: name}
s.DefaultIO()
return s
}
// Init initializes the config and client for the State.
func (s *State) Init(config upspin.Config) {
var cl upspin.Client
if config != nil {
cl = client.New(config)
}
s.Config = config
s.Client = cl
}
func (s *State) SetIO(stdin io.Reader, stdout, stderr io.Writer) {
s.Stdin = stdin
s.Stdout = stdout
s.Stderr = stderr
}
func (s *State) DefaultIO() {
s.SetIO(os.Stdin, os.Stdout, os.Stderr)
}
// Exitf prints the error and exits the program.
// If we are interactive, it calls panic("exit"), which is intended to be recovered
// from by the calling interpreter.
// We don't use log (although the packages we call do) because the errors
// are for regular people.
func (s *State) Exitf(format string, args ...interface{}) {
format = fmt.Sprintf("upspin: %s: %s\n", s.Name, format)
fmt.Fprintf(s.Stderr, format, args...)
if s.Interactive {
panic("exit")
}
s.ExitCode = 1
s.ExitNow()
}
// Exit calls s.Exitf with the error.
func (s *State) Exit(err error) {
s.Exitf("%s", err)
}
// ExitNow terminates the process with the current ExitCode.
func (s *State) ExitNow() {
shutdown.Now(s.ExitCode)
}
// Failf logs the error and sets the exit code. It does not exit the program.
func (s *State) Failf(format string, args ...interface{}) {
format = fmt.Sprintf("upspin: %s: %s\n", s.Name, format)
fmt.Fprintf(s.Stderr, format, args...)
s.ExitCode = 1
}
// Fail calls s.Failf with the error.
func (s *State) Fail(err error) {
s.Failf("%v", err)
}
// KeyServer returns the KeyServer for the root of the name, or exits on failure.
func (s *State) KeyServer() upspin.KeyServer {
key, err := bind.KeyServer(s.Config, s.Config.KeyEndpoint())
if err != nil {
s.Exit(err)
}
return key
}
// DirServer returns the DirServer for the root of the name, or exits on failure.
func (s *State) DirServer(name upspin.PathName) upspin.DirServer {
dir, err := s.Client.DirServer(name)
if err != nil {
s.Exit(err)
}
return dir
}