Skip to content

Commit

Permalink
👔 up(cli/cmdline): use strings.Builder refactor the cmdline.Builder l…
Browse files Browse the repository at this point in the history
…ogic

- add more tests
  • Loading branch information
inhere committed Mar 6, 2023
1 parent ccf2430 commit 7060159
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 43 deletions.
75 changes: 32 additions & 43 deletions cliutil/cmdline/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package cmdline

import (
"strings"

"github.com/gookit/goutil/strutil"
)

// LineBuilder build command line string.
// codes refer from strings.Builder
type LineBuilder struct {
buf []byte
strings.Builder
}

// NewBuilder create
Expand All @@ -17,16 +19,11 @@ func NewBuilder(binFile string, args ...string) *LineBuilder {
if binFile != "" {
b.AddArg(binFile)
}
b.AddArray(args)

b.AddArray(args)
return b
}

// LineBuild build command line string by given args.
func LineBuild(binFile string, args []string) string {
return NewBuilder(binFile, args...).String()
}

// AddArg to builder
func (b *LineBuilder) AddArg(arg string) {
_, _ = b.WriteString(arg)
Expand All @@ -44,53 +41,45 @@ func (b *LineBuilder) AddArray(args []string) {
}
}

// AddAny args to builder
func (b *LineBuilder) AddAny(args ...any) {
for _, arg := range args {
_, _ = b.WriteString(strutil.SafeString(arg))
}
}

// WriteString arg string to the builder, will auto quote special string.
// refer strconv.Quote()
func (b *LineBuilder) WriteString(a string) (int, error) {
var quote byte
if strings.ContainsRune(a, '"') {
if pos := strings.IndexByte(a, '"'); pos > -1 {
quote = '\''
} else if a == "" || strings.ContainsRune(a, '\'') || strings.ContainsRune(a, ' ') {
// fix: a = `--pretty=format:"one two three"`
if pos > 0 && '"' == a[len(a)-1] {
quote = 0
}
} else if pos := strings.IndexByte(a, '\''); pos > -1 {
quote = '"'
// fix: a = "--pretty=format:'one two three'"
if pos > 0 && '\'' == a[len(a)-1] {
quote = 0
}
} else if a == "" || strings.ContainsRune(a, ' ') {
quote = '"'
}

// add sep on first write.
if b.buf != nil {
b.buf = make([]byte, 24)
b.buf = append(b.buf, ' ')
// add sep on not-first write.
if b.Len() != 0 {
_ = b.WriteByte(' ')
}

// no quote char
// no quote char OR not need quote
if quote == 0 {
b.buf = append(b.buf, a...)
return len(a) + 1, nil
return b.Builder.WriteString(a)
}

b.buf = append(b.buf, quote) // add start quote
b.buf = append(b.buf, a...)
b.buf = append(b.buf, quote) // add end quote
return len(a) + 3, nil
}

// String to command line string
func (b *LineBuilder) String() string {
return string(b.buf)
}

// Len of the builder
func (b *LineBuilder) Len() int {
return len(b.buf)
}

// Reset builder
func (b *LineBuilder) Reset() {
b.buf = nil
_ = b.WriteByte(quote) // add start quote
n, err := b.Builder.WriteString(a)
_ = b.WriteByte(quote) // add end quote
return n, err
}

// grow copies the buffer to a new, larger buffer so that there are at least n
// bytes of capacity beyond len(b.buf).
// func (b *LineBuilder) grow(n int) {
// buf := make([]byte, len(b.buf), 2*cap(b.buf)+n)
// copy(buf, b.buf)
// b.buf = buf
// }
7 changes: 7 additions & 0 deletions cliutil/cmdline/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,10 @@ func TestLineBuild(t *testing.T) {
b.AddArgs("myapp", "-a", `the 'val0' of option`)
assert.Eq(t, `myapp -a "the 'val0' of option"`, b.String())
}

func TestLineBuild_hasQuote(t *testing.T) {
line := "git log --pretty=format:'one two three'"
args := cmdline.ParseLine(line)
// dump.P(args)
assert.Eq(t, line, cmdline.LineBuild("", args))
}
10 changes: 10 additions & 0 deletions cliutil/cmdline/cmdline.go
Original file line number Diff line number Diff line change
@@ -1,2 +1,12 @@
// Package cmdline provide quick build and parse cmd line string.
package cmdline

// LineBuild build command line string by given args.
func LineBuild(binFile string, args []string) string {
return NewBuilder(binFile, args...).String()
}

// ParseLine input command line text. alias of the StringToOSArgs()
func ParseLine(line string) []string {
return NewParser(line).Parse()
}

0 comments on commit 7060159

Please sign in to comment.