Skip to content

Commit

Permalink
Initial version
Browse files Browse the repository at this point in the history
  • Loading branch information
muraliens committed Oct 19, 2023
1 parent 2915fb0 commit a8b697b
Show file tree
Hide file tree
Showing 4 changed files with 301 additions and 0 deletions.
191 changes: 191 additions & 0 deletions cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
package enscli

import (
"flag"
"os"
"runtime"
"strings"

"github.com/EnsurityTechnologies/logger"
)

const (
StringType string = "string"
IntType string = "int"
Int64Type string = "int64"
UIntType string = "uint"
UInt64Type string = "uint64"
Float64Type string = "float64"
BoolType string = "bool"
)

type CommandHandler func() bool

type Function struct {
Handler CommandHandler
Title string
SuccessMsg string
FailureMsg string
}

type Option struct {
Type string
Flag string
Ptr interface{}
Default interface{}
Usage string
}

type EnsCli struct {
name string
log logger.Logger
funcs map[string]*Function
options []*Option
}

func NewEnsCli(name string, log logger.Logger) (*EnsCli, error) {
return &EnsCli{name: name, log: log, funcs: make(map[string]*Function), options: make([]*Option, 0)}, nil
}

func (cli *EnsCli) AddCommand(cmd string, f *Function) {
cmd = strings.ToLower(cmd)
cli.funcs[cmd] = f
}

func (cli *EnsCli) AddOption(o *Option) {
o.Type = strings.ToLower(o.Type)
cli.options = append(cli.options, o)
}

func (cli *EnsCli) showHelp() {
msg := "Command line helper\n\n"
if runtime.GOOS == "windows" {
msg = msg + cli.name + ".exe <cmd>\n\n"
} else {
msg = msg + cli.name + " <cmd>\n\n"
}
msg = msg + "Use the following commands\n\n"
for k := range cli.funcs {
msg = msg + " " + k + "\n"
}
msg = msg + "\nSupported options\n\n"
for _, o := range cli.options {
msg = msg + " " + o.Flag + " <" + o.Type + ">" + " : " + o.Usage + "\n"
}
cli.log.Info(msg)
}

func (cli *EnsCli) Run() {
if len(os.Args) < 2 {
cli.showHelp()
return
}
cmd := strings.ToLower(os.Args[1])
if cmd == "-h" || cmd == "-help" {
cli.showHelp()
return
}
f, ok := cli.funcs[cmd]
if !ok {
cli.log.Error("Unsupported command, please check the helper")
cli.showHelp()
return
}
os.Args = os.Args[1:]
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
for _, o := range cli.options {
switch o.Type {
case StringType:
ptr, ok := o.Ptr.(*string)
if !ok {
cli.log.Error("invalid pointer, expected string pointer", "flag", o.Flag)
return
}
v, ok := o.Default.(string)
if !ok {
cli.log.Error("invalid default value, expected string", "flag", o.Flag)
return
}
flag.StringVar(ptr, o.Flag, v, o.Usage)
case IntType:
ptr, ok := o.Ptr.(*int)
if !ok {
cli.log.Error("invalid pointer, expected integer pointer", "flag", o.Flag)
return
}
v, ok := o.Default.(int)
if !ok {
cli.log.Error("invalid default value, expected integer", "flag", o.Flag)
return
}
flag.IntVar(ptr, o.Flag, v, o.Usage)
case Int64Type:
ptr, ok := o.Ptr.(*int64)
if !ok {
cli.log.Error("invalid pointer, expected integer 64 pointer", "flag", o.Flag)
return
}
v, ok := o.Default.(int64)
if !ok {
cli.log.Error("invalid default value, expected integer 64", "flag", o.Flag)
return
}
flag.Int64Var(ptr, o.Flag, v, o.Usage)
case UIntType:
ptr, ok := o.Ptr.(*uint)
if !ok {
cli.log.Error("invalid pointer, expected unsigned integer pointer", "flag", o.Flag)
return
}
v, ok := o.Default.(uint)
if !ok {
cli.log.Error("invalid default value, expected unsigned integer", "flag", o.Flag)
return
}
flag.UintVar(ptr, o.Flag, v, o.Usage)
case UInt64Type:
ptr, ok := o.Ptr.(*uint64)
if !ok {
cli.log.Error("invalid pointer, expected unsigned integer 64 pointer", "flag", o.Flag)
return
}
v, ok := o.Default.(uint64)
if !ok {
cli.log.Error("invalid default value, expected unsigned integer 64", "flag", o.Flag)
return
}
flag.Uint64Var(ptr, o.Flag, v, o.Usage)
case Float64Type:
ptr, ok := o.Ptr.(*float64)
if !ok {
cli.log.Error("invalid pointer, expected float 64 pointer", "flag", o.Flag)
return
}
v, ok := o.Default.(float64)
if !ok {
cli.log.Error("invalid default value, expected float 64", "flag", o.Flag)
return
}
flag.Float64Var(ptr, o.Flag, v, o.Usage)
case BoolType:
ptr, ok := o.Ptr.(*bool)
if !ok {
cli.log.Error("invalid pointer, expected boolean pointer", "flag", o.Flag)
return
}
v, ok := o.Default.(bool)
if !ok {
cli.log.Error("invalid default value, expected boolean", "flag", o.Flag)
return
}
flag.BoolVar(ptr, o.Flag, v, o.Usage)
}
}
flag.Parse()
cli.log.Info("Executing the function : " + f.Title)
if !f.Handler() {
cli.log.Error(f.FailureMsg)
} else {
cli.log.Info(f.SuccessMsg)
}
}
86 changes: 86 additions & 0 deletions cli_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package enscli

import (
"io"
"os"
"testing"

"github.com/EnsurityTechnologies/logger"
)

type TestCli struct {
EnsCli
log logger.Logger
msg string
}

func (tc *TestCli) statusFunction() bool {
tc.log.Info("CLI is running fine")
return true
}

func (tc *TestCli) echoFunction() bool {
tc.log.Info(tc.msg)
return true
}

func TestBasic(t *testing.T) {
fp, err := os.OpenFile("log.txt",
os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
panic(err)
}
logOptions := &logger.LoggerOptions{
Name: "TestCLI",
Color: []logger.ColorOption{logger.AutoColor, logger.ColorOff},
Output: []io.Writer{logger.DefaultOutput, fp},
}

log := logger.New(logOptions)
tc := &TestCli{
log: log,
}
cli, err := NewEnsCli("test", log.Named("cli"))
if err != nil {
t.Fatal("failed to create cli")
}
sf := &Function{
Handler: tc.statusFunction,
Title: "Status function",
SuccessMsg: "Status returned successfully",
FailureMsg: "Status failed",
}
ef := &Function{
Handler: tc.echoFunction,
Title: "Echo function",
SuccessMsg: "Echo returned successfully",
FailureMsg: "Echo failed",
}
cli.AddCommand("status", sf)
cli.AddCommand("echo", ef)
o := &Option{
Type: StringType,
Flag: "m",
Ptr: &tc.msg,
Default: "Test message",
Usage: "Display message",
}
cli.AddOption(o)
os.Args = make([]string, 1)
os.Args[0] = "test"
cli.Run()
os.Args = make([]string, 2)
os.Args[0] = "test"
os.Args[1] = "test"
cli.Run()
os.Args = make([]string, 2)
os.Args[0] = "test"
os.Args[1] = "status"
cli.Run()
os.Args = make([]string, 4)
os.Args[0] = "test"
os.Args[1] = "echo"
os.Args[2] = "-m"
os.Args[3] = "CLI Test message"
cli.Run()
}
12 changes: 12 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module github.com/EnsurityTechnologies/enscli

go 1.20

require github.com/EnsurityTechnologies/logger v1.0.4

require (
github.com/fatih/color v1.12.0 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-isatty v0.0.13 // indirect
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae // indirect
)
12 changes: 12 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
github.com/EnsurityTechnologies/logger v1.0.4 h1:LG9xUEC9PIA1EngzeUZi+MyD5cdfAnsHw8XF5hKXFzY=
github.com/EnsurityTechnologies/logger v1.0.4/go.mod h1:d/0zwSaO0tDbl4wd4QvXtYPV4QpFDpbgAPcTDMRPuOU=
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

0 comments on commit a8b697b

Please sign in to comment.