github.com/moqsien/xraycore@v1.8.5/main/commands/base/command.go (about)

     1  // Copyright 2017 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package base defines shared basic pieces of the commands,
     6  // in particular logging and the Command structure.
     7  package base
     8  
     9  import (
    10  	"flag"
    11  	"fmt"
    12  	"os"
    13  	"strings"
    14  	"sync"
    15  )
    16  
    17  // A Command is an implementation of a xray command
    18  // like xray run or xray version.
    19  type Command struct {
    20  	// Run runs the command.
    21  	// The args are the arguments after the command name.
    22  	Run func(cmd *Command, args []string)
    23  
    24  	// UsageLine is the one-line usage message.
    25  	// The words between the first word (the "executable name") and the first flag or argument in the line are taken to be the command name.
    26  	//
    27  	// UsageLine supports go template syntax. It's recommended to use "{{.Exec}}" instead of hardcoding name
    28  	UsageLine string
    29  
    30  	// Short is the short description shown in the 'go help' output.
    31  	//
    32  	// Note: Short does not support go template syntax.
    33  	Short string
    34  
    35  	// Long is the long message shown in the 'go help <this-command>' output.
    36  	//
    37  	// Long supports go template syntax. It's recommended to use "{{.Exec}}", "{{.LongName}}" instead of hardcoding strings
    38  	Long string
    39  
    40  	// Flag is a set of flags specific to this command.
    41  	Flag flag.FlagSet
    42  
    43  	// CustomFlags indicates that the command will do its own
    44  	// flag parsing.
    45  	CustomFlags bool
    46  
    47  	// Commands lists the available commands and help topics.
    48  	// The order here is the order in which they are printed by 'go help'.
    49  	// Note that subcommands are in general best avoided.
    50  	Commands []*Command
    51  }
    52  
    53  // LongName returns the command's long name: all the words in the usage line between first word (e.g. "xray") and a flag or argument,
    54  func (c *Command) LongName() string {
    55  	name := c.UsageLine
    56  	if i := strings.Index(name, " ["); i >= 0 {
    57  		name = strings.TrimSpace(name[:i])
    58  	}
    59  	if i := strings.Index(name, " "); i >= 0 {
    60  		name = name[i+1:]
    61  	} else {
    62  		name = ""
    63  	}
    64  	return strings.TrimSpace(name)
    65  }
    66  
    67  // Name returns the command's short name: the last word in the usage line before a flag or argument.
    68  func (c *Command) Name() string {
    69  	name := c.LongName()
    70  	if i := strings.LastIndex(name, " "); i >= 0 {
    71  		name = name[i+1:]
    72  	}
    73  	return strings.TrimSpace(name)
    74  }
    75  
    76  // Usage prints usage of the Command
    77  func (c *Command) Usage() {
    78  	buildCommandText(c)
    79  	fmt.Fprintf(os.Stderr, "usage: %s\n", c.UsageLine)
    80  	fmt.Fprintf(os.Stderr, "Run 'xray help %s' for details.\n", c.LongName())
    81  	SetExitStatus(2)
    82  	Exit()
    83  }
    84  
    85  // Runnable reports whether the command can be run; otherwise
    86  // it is a documentation pseudo-command such as importpath.
    87  func (c *Command) Runnable() bool {
    88  	return c.Run != nil
    89  }
    90  
    91  // Exit exits with code set with SetExitStatus()
    92  func Exit() {
    93  	os.Exit(exitStatus)
    94  }
    95  
    96  // Fatalf logs error and exit with code 1
    97  func Fatalf(format string, args ...interface{}) {
    98  	Errorf(format, args...)
    99  	Exit()
   100  }
   101  
   102  // Errorf logs error and set exit status to 1, but not exit
   103  func Errorf(format string, args ...interface{}) {
   104  	fmt.Fprintf(os.Stderr, format, args...)
   105  	fmt.Fprintln(os.Stderr)
   106  	SetExitStatus(1)
   107  }
   108  
   109  // ExitIfErrors exits if current status is not zero
   110  func ExitIfErrors() {
   111  	if exitStatus != 0 {
   112  		Exit()
   113  	}
   114  }
   115  
   116  var (
   117  	exitStatus = 0
   118  	exitMu     sync.Mutex
   119  )
   120  
   121  // SetExitStatus set exit status code
   122  func SetExitStatus(n int) {
   123  	exitMu.Lock()
   124  	if exitStatus < n {
   125  		exitStatus = n
   126  	}
   127  	exitMu.Unlock()
   128  }
   129  
   130  // GetExitStatus get exit status code
   131  func GetExitStatus() int {
   132  	return exitStatus
   133  }