Skip to content

Commit 32188ec

Browse files
refactor main, add gosec in linter options (#6)
1 parent 269ebc3 commit 32188ec

7 files changed

Lines changed: 190 additions & 152 deletions

File tree

.golangci.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ linters:
77
# See the comment on top of this file.
88
enable:
99
- errcheck
10+
- gosec

cmd/main.go

Lines changed: 4 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -4,151 +4,13 @@
44
package main
55

66
import (
7-
"encoding/json"
8-
"errors"
9-
"io"
107
"os"
11-
"strings"
12-
13-
"tcli/internal/cmd"
14-
"tcli/internal/common"
15-
"tcli/internal/config"
16-
"tcli/internal/parser"
17-
)
18-
19-
const (
20-
cmdModules = 1
21-
cmdCommands = 2
22-
cmdCommand = 3
23-
24-
//
25-
posModule = 1
26-
posCommand = 2
27-
posSubCommand = 3
28-
)
29-
30-
type cmdState struct {
31-
state int
32-
module string
33-
cmd string
34-
subCmd string
35-
args []string
36-
}
37-
38-
type module struct {
39-
Tag parser.Tag
40-
}
41-
42-
const (
43-
defaultInput = "data/example.json"
44-
envOpenApiFile = "OPENAPI_FILE"
45-
)
46-
47-
var (
48-
modules []module
49-
argc = len(os.Args)
50-
state = getCmdState()
8+
"tcli/internal/env"
519
)
5210

11+
// main is the entry point for the tcli application.
5312
func main() {
54-
var err error
55-
if !config.Load() {
56-
panic("config load failed")
57-
}
58-
// handle command line args
59-
switch state.state {
60-
case cmdModules:
61-
config.ShowModules()
62-
case cmdCommands:
63-
err = config.ShowCommands(state.module)
64-
case cmdCommand:
65-
err = call(config.ShowCommand(state.module, state.cmd, state.subCmd))
66-
}
67-
handleError(err)
68-
}
69-
70-
func call(m *parser.Method, err error) error {
71-
if err != nil {
72-
return err
73-
}
74-
75-
env := cmd.GetExecutionEnv(state.args)
76-
77-
// read from stdin and feed to commands
78-
if hasStdin() {
79-
input := json.NewDecoder(os.Stdin)
80-
var any common.Input
81-
for err == nil {
82-
err = input.Decode(&any)
83-
if err == io.EOF {
84-
break
85-
}
86-
err = env.Exec(m, &any)
87-
}
88-
} else {
89-
err = env.Exec(m, nil)
90-
}
91-
if err != nil {
92-
return err
93-
}
94-
return env.Wait()
95-
}
96-
97-
func getCmdState() cmdState {
98-
if argc < 2 {
99-
return cmdState{state: cmdModules}
100-
} else if argc < 3 {
101-
return cmdState{state: cmdCommands, module: os.Args[posModule]}
102-
} else {
103-
subCmd := getSubCmd()
104-
args := os.Args[3:]
105-
if subCmd != "" {
106-
args = os.Args[4:]
107-
}
108-
return cmdState{
109-
state: cmdCommand,
110-
module: os.Args[posModule],
111-
cmd: os.Args[posCommand],
112-
subCmd: subCmd,
113-
args: args}
114-
}
115-
}
116-
117-
// subcommands are present when an api has a tagged set of commands
118-
// this will be the final level of supported drill in for commands
119-
func getSubCmd() string {
120-
if argc > posSubCommand {
121-
temp := os.Args[posSubCommand]
122-
if !strings.HasPrefix(temp, "-") {
123-
return temp
124-
}
125-
}
126-
return ""
127-
}
128-
129-
// Attempt to avoid specifying a flag for stdin
130-
// check if there is something readable at stdin
131-
// this is not tested beyond minimal redirection
132-
// it is tested in windows as well but there could be
133-
// corner cases when it doesnt work. If that is the case
134-
// it's better to specify a flag to indicate input
135-
func hasStdin() bool {
136-
fi, err := os.Stdin.Stat()
137-
if err != nil {
138-
return false
139-
}
140-
return fi.Mode()&os.ModeCharDevice == 0
141-
}
142-
143-
func handleError(err error) {
144-
if err == nil {
145-
return
146-
}
147-
if errors.Is(err, common.ErrNoSuchModule) {
148-
config.ShowModules()
149-
} else if errors.Is(err, common.ErrNoSuchCommand) {
150-
err = config.ShowCommands(state.module)
151-
} else if errors.Is(err, common.ErrNoSuchSubCommand) {
152-
config.ShowTaggedCommands(state.cmd)
13+
if err := env.Run(); err != nil {
14+
os.Exit(1)
15315
}
15416
}

internal/cmd/http.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func (c *HttpCommand) http() error {
5858
utils.AddAuthorizationHeader(req, *p.Values[JwtParam])
5959
}
6060

61-
client := utils.RetriableClient(p.Global.RetryCount)
61+
client := utils.RetriableClient(int64(p.Global.RetryCount))
6262
resp, err := client.Do(req)
6363
if err != nil {
6464
log.Fatalf("Response error: %v\n", err)

internal/cmd/params.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const GlobalFlags = "global"
2121

2222
type GlobalResult struct {
2323
Count uint
24-
RetryCount uint
24+
RetryCount int64
2525
BasePath string
2626
Scheme string
2727
Server string
@@ -47,6 +47,8 @@ type paramsResult struct {
4747
headers http.Header
4848
}
4949

50+
// New creates a new ParseResult and initializes the flag set
51+
// with the parameters from the given method and global flags.
5052
func New(o *parser.Method, in *common.Input, r *parser.Root) *ParseResult {
5153
fs := flag.NewFlagSet(o.OperationId, flag.ExitOnError)
5254

@@ -71,6 +73,8 @@ func New(o *parser.Method, in *common.Input, r *parser.Root) *ParseResult {
7173
return &p
7274
}
7375

76+
// getParamVal returns the value of the parameter from the input map
77+
// if it exists, otherwise it returns the default value of the parameter.
7478
func getParamVal(p *parser.Parameter, i *common.Input) string {
7579
if i != nil {
7680
if input, ok := (*i)[p.Name]; ok {
@@ -83,12 +87,13 @@ func getParamVal(p *parser.Parameter, i *common.Input) string {
8387
return p.DefaultStr()
8488
}
8589

90+
// getGlobal initializes the global flags and returns a GlobalResult.
8691
func getGlobal(fs *flag.FlagSet, r *parser.Root) *GlobalResult {
8792
g := GlobalResult{}
8893
fs.StringVar(&g.BasePath, "base_path", getBasePath(r), "http base path")
8994
fs.StringVar(&g.Doc, "doc", "none", "Generate docs (none, shell)")
9095
fs.StringVar(&g.Format, "format", "", "json format")
91-
fs.UintVar(&g.RetryCount, "retry_count", 10, "Number of retries on failure")
96+
fs.Int64Var(&g.RetryCount, "retry_count", 10, "Number of retries on failure")
9297
fs.StringVar(&g.Scheme, "scheme", getScheme(r), "Scheme")
9398
fs.StringVar(&g.Server, "server", getHost(r), "Server")
9499
fs.UintVar(&g.Count, "count", 1, "Number of times to repeat command")
@@ -124,6 +129,8 @@ func getScheme(r *parser.Root) string {
124129
}
125130
}
126131

132+
// ValidateParams checks that all required parameters have values
133+
// and sets global configuration options based on the parsed values.
127134
func (p *ParseResult) ValidateParams() error {
128135
for _, v := range p.Method.Parameters {
129136
val, ok := p.Values[v.Name]
@@ -143,6 +150,8 @@ func (p *ParseResult) ValidateParams() error {
143150
return nil
144151
}
145152

153+
// getParamValues processes the parameters and returns a paramsResult
154+
// containing the URL values, path, body, and headers for the HTTP request.
146155
func (p *ParseResult) getParamValues() (*paramsResult, error) {
147156
result := paramsResult{
148157
urlValues: &url.Values{},

internal/env/run.go

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
// Copyright 2025 HP Development Company, L.P.
2+
// SPDX-License-Identifier: MIT
3+
4+
package env
5+
6+
import (
7+
"encoding/json"
8+
"errors"
9+
"io"
10+
"os"
11+
"strings"
12+
13+
"tcli/internal/cmd"
14+
"tcli/internal/common"
15+
"tcli/internal/config"
16+
"tcli/internal/parser"
17+
)
18+
19+
const (
20+
cmdModules = 1
21+
cmdCommands = 2
22+
cmdCommand = 3
23+
24+
//
25+
posModule = 1
26+
posCommand = 2
27+
posSubCommand = 3
28+
)
29+
30+
type cmdState struct {
31+
state int
32+
module string
33+
cmd string
34+
subCmd string
35+
args []string
36+
}
37+
38+
type module struct {
39+
Tag parser.Tag
40+
}
41+
42+
var (
43+
modules []module
44+
argc = len(os.Args)
45+
state = getCmdState()
46+
)
47+
48+
// Run is the main entry point for executing commands based on command line arguments
49+
func Run() error {
50+
var err error
51+
if !config.Load() {
52+
return errors.New("config load failed")
53+
}
54+
// handle command line args
55+
switch state.state {
56+
case cmdModules:
57+
config.ShowModules()
58+
case cmdCommands:
59+
err = config.ShowCommands(state.module)
60+
case cmdCommand:
61+
err = call(config.ShowCommand(state.module, state.cmd, state.subCmd))
62+
}
63+
return handleError(err)
64+
}
65+
66+
// call executes the given method with the provided error handling
67+
func call(m *parser.Method, err error) error {
68+
if err != nil {
69+
return err
70+
}
71+
72+
env := cmd.GetExecutionEnv(state.args)
73+
74+
// read from stdin and feed to commands
75+
if hasStdin() {
76+
input := json.NewDecoder(os.Stdin)
77+
var any common.Input
78+
for err == nil {
79+
err = input.Decode(&any)
80+
if err == io.EOF {
81+
break
82+
}
83+
err = env.Exec(m, &any)
84+
}
85+
} else {
86+
err = env.Exec(m, nil)
87+
}
88+
return env.Wait()
89+
}
90+
91+
// getCmdState determines the current command state based on command line arguments
92+
func getCmdState() cmdState {
93+
if argc < 2 {
94+
return cmdState{state: cmdModules}
95+
} else if argc < 3 {
96+
return cmdState{state: cmdCommands, module: os.Args[posModule]}
97+
} else {
98+
subCmd := getSubCmd()
99+
args := os.Args[3:]
100+
if subCmd != "" {
101+
args = os.Args[4:]
102+
}
103+
return cmdState{
104+
state: cmdCommand,
105+
module: os.Args[posModule],
106+
cmd: os.Args[posCommand],
107+
subCmd: subCmd,
108+
args: args}
109+
}
110+
}
111+
112+
// subcommands are present when an api has a tagged set of commands
113+
// this will be the final level of supported drill in for commands
114+
func getSubCmd() string {
115+
if argc > posSubCommand {
116+
temp := os.Args[posSubCommand]
117+
if !strings.HasPrefix(temp, "-") {
118+
return temp
119+
}
120+
}
121+
return ""
122+
}
123+
124+
// Attempt to avoid specifying a flag for stdin
125+
// check if there is something readable at stdin
126+
// this is not tested beyond minimal redirection
127+
// it is tested in windows as well but there could be
128+
// corner cases when it doesnt work. If that is the case
129+
// it's better to specify a flag to indicate input
130+
func hasStdin() bool {
131+
fi, err := os.Stdin.Stat()
132+
if err != nil {
133+
return false
134+
}
135+
return fi.Mode()&os.ModeCharDevice == 0
136+
}
137+
138+
// handleError processes errors and displays appropriate information
139+
func handleError(err error) error {
140+
if err == nil {
141+
return nil
142+
}
143+
if errors.Is(err, common.ErrNoSuchModule) {
144+
config.ShowModules()
145+
} else if errors.Is(err, common.ErrNoSuchCommand) {
146+
err = config.ShowCommands(state.module)
147+
} else if errors.Is(err, common.ErrNoSuchSubCommand) {
148+
config.ShowTaggedCommands(state.cmd)
149+
} else {
150+
return err
151+
}
152+
return nil
153+
}

0 commit comments

Comments
 (0)