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 }