Files
EdgeNode/internal/apps/app_cmd.go
2020-09-09 18:53:53 +08:00

235 lines
4.2 KiB
Go

package apps
import (
"fmt"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/logs"
"os"
"os/exec"
"runtime"
"strconv"
"time"
)
// App命令帮助
type AppCmd struct {
product string
version string
usage string
options []*CommandHelpOption
appendStrings []string
directives []*Directive
}
func NewAppCmd() *AppCmd {
return &AppCmd{}
}
type CommandHelpOption struct {
Code string
Description string
}
// 产品
func (this *AppCmd) Product(product string) *AppCmd {
this.product = product
return this
}
// 版本
func (this *AppCmd) Version(version string) *AppCmd {
this.version = version
return this
}
// 使用方法
func (this *AppCmd) Usage(usage string) *AppCmd {
this.usage = usage
return this
}
// 选项
func (this *AppCmd) Option(code string, description string) *AppCmd {
this.options = append(this.options, &CommandHelpOption{
Code: code,
Description: description,
})
return this
}
// 附加内容
func (this *AppCmd) Append(appendString string) *AppCmd {
this.appendStrings = append(this.appendStrings, appendString)
return this
}
// 打印
func (this *AppCmd) Print() {
fmt.Println(this.product + " v" + this.version)
usage := this.usage
fmt.Println("Usage:", "\n "+usage)
if len(this.options) > 0 {
fmt.Println("")
fmt.Println("Options:")
spaces := 20
max := 40
for _, option := range this.options {
l := len(option.Code)
if l < max && l > spaces {
spaces = l + 4
}
}
for _, option := range this.options {
if len(option.Code) > max {
fmt.Println("")
fmt.Println(" " + option.Code)
option.Code = ""
}
fmt.Printf(" %-"+strconv.Itoa(spaces)+"s%s\n", option.Code, ": "+option.Description)
}
}
if len(this.appendStrings) > 0 {
fmt.Println("")
for _, s := range this.appendStrings {
fmt.Println(s)
}
}
}
// 添加指令
func (this *AppCmd) On(arg string, callback func()) {
this.directives = append(this.directives, &Directive{
Arg: arg,
Callback: callback,
})
}
// 运行
func (this *AppCmd) Run(main func()) {
// 获取参数
args := os.Args[1:]
if len(args) > 0 {
switch args[0] {
case "-v", "version", "-version", "--version":
this.runVersion()
return
case "?", "help", "-help", "h", "-h":
this.runHelp()
return
case "start":
this.runStart()
return
case "stop":
this.runStop()
return
case "restart":
this.runRestart()
return
case "status":
this.runStatus()
return
}
// 查找指令
for _, directive := range this.directives {
if directive.Arg == args[0] {
directive.Callback()
return
}
}
fmt.Println("unknown command '" + args[0] + "'")
return
}
// 记录PID
_ = this.writePid()
// 日志
writer := new(LogWriter)
writer.Init()
logs.SetWriter(writer)
// 运行主函数
main()
}
// 版本号
func (this *AppCmd) runVersion() {
fmt.Println(this.product+" v"+this.version, "(build: "+runtime.Version(), runtime.GOOS, runtime.GOARCH+")")
}
// 帮助
func (this *AppCmd) runHelp() {
this.Print()
}
// 启动
func (this *AppCmd) runStart() {
proc := this.checkPid()
if proc != nil {
fmt.Println(this.product+" already started, pid:", proc.Pid)
return
}
cmd := exec.Command(os.Args[0])
err := cmd.Start()
if err != nil {
fmt.Println(this.product+" start failed:", err.Error())
return
}
fmt.Println(this.product+" started ok, pid:", cmd.Process.Pid)
}
// 停止
func (this *AppCmd) runStop() {
proc := this.checkPid()
if proc == nil {
fmt.Println(this.product + " not started yet")
return
}
// 停止进程
_ = proc.Kill()
// 在Windows上经常不能及时释放资源
_ = DeletePid(Tea.Root + "/bin/pid")
fmt.Println(this.product+" stopped ok, pid:", proc.Pid)
}
// 重启
func (this *AppCmd) runRestart() {
this.runStop()
time.Sleep(1 * time.Second)
this.runStart()
}
// 状态
func (this *AppCmd) runStatus() {
proc := this.checkPid()
if proc == nil {
fmt.Println(this.product + " not started yet")
} else {
fmt.Println(this.product + " is running, pid: " + fmt.Sprintf("%d", proc.Pid))
}
}
// 检查PID
func (this *AppCmd) checkPid() *os.Process {
return CheckPid(Tea.Root + "/bin/pid")
}
// 写入PID
func (this *AppCmd) writePid() error {
return WritePid(Tea.Root + "/bin/pid")
}