feat: utils.go — FilterArgs, ParseFlag with short/long flag rules
- FilterArgs: removes empty strings and Go test runner flags
- ParseFlag: single dash (-v, -🔥) must be 1 char, double dash (--verbose) must be 2+ chars
- Cli.Run() now uses FilterArgs and ParseFlag — no test flag awareness in surface layer
- Invalid flags silently ignored (e.g. -verbose, --v)
221 tests, 79.7% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
c61a2d3dfe
commit
8854d5c79f
3 changed files with 153 additions and 20 deletions
|
|
@ -34,13 +34,7 @@ func (cl *Cli) Run(args ...string) Result[any] {
|
|||
args = os.Args[1:]
|
||||
}
|
||||
|
||||
// Filter out empty args and test flags
|
||||
var clean []string
|
||||
for _, a := range args {
|
||||
if a != "" && !strings.HasPrefix(a, "-test.") {
|
||||
clean = append(clean, a)
|
||||
}
|
||||
}
|
||||
clean := FilterArgs(args)
|
||||
|
||||
if cl.core == nil || cl.core.commands == nil || len(cl.core.commands.commands) == 0 {
|
||||
// No commands registered — print banner and exit
|
||||
|
|
@ -76,23 +70,17 @@ func (cl *Cli) Run(args ...string) Result[any] {
|
|||
// Build options from remaining args (flags become Options)
|
||||
opts := Options{}
|
||||
for _, arg := range remaining {
|
||||
if strings.HasPrefix(arg, "--") {
|
||||
parts := strings.SplitN(strings.TrimPrefix(arg, "--"), "=", 2)
|
||||
if len(parts) == 2 {
|
||||
opts = append(opts, Option{K: parts[0], V: parts[1]})
|
||||
key, val, valid := ParseFlag(arg)
|
||||
if valid {
|
||||
if val != "" {
|
||||
opts = append(opts, Option{K: key, V: val})
|
||||
} else {
|
||||
opts = append(opts, Option{K: parts[0], V: true})
|
||||
opts = append(opts, Option{K: key, V: true})
|
||||
}
|
||||
} else if strings.HasPrefix(arg, "-") {
|
||||
parts := strings.SplitN(strings.TrimPrefix(arg, "-"), "=", 2)
|
||||
if len(parts) == 2 {
|
||||
opts = append(opts, Option{K: parts[0], V: parts[1]})
|
||||
} else {
|
||||
opts = append(opts, Option{K: parts[0], V: true})
|
||||
}
|
||||
} else {
|
||||
} else if !strings.HasPrefix(arg, "-") {
|
||||
opts = append(opts, Option{K: "_arg", V: arg})
|
||||
}
|
||||
// Invalid flags (e.g. -verbose, --v) are silently ignored
|
||||
}
|
||||
|
||||
return cmd.Run(opts)
|
||||
|
|
|
|||
67
pkg/core/utils.go
Normal file
67
pkg/core/utils.go
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
// Utility functions for the Core framework.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// FilterArgs removes empty strings and Go test runner flags from an argument list.
|
||||
//
|
||||
// clean := core.FilterArgs(os.Args[1:])
|
||||
func FilterArgs(args []string) []string {
|
||||
var clean []string
|
||||
for _, a := range args {
|
||||
if a == "" || strings.HasPrefix(a, "-test.") {
|
||||
continue
|
||||
}
|
||||
clean = append(clean, a)
|
||||
}
|
||||
return clean
|
||||
}
|
||||
|
||||
// ParseFlag parses a single flag argument into key, value, and validity.
|
||||
// Single dash (-) requires exactly 1 character (letter, emoji, unicode).
|
||||
// Double dash (--) requires 2+ characters.
|
||||
//
|
||||
// "-v" → "v", "", true
|
||||
// "-🔥" → "🔥", "", true
|
||||
// "--verbose" → "verbose", "", true
|
||||
// "--port=8080" → "port", "8080", true
|
||||
// "-verbose" → "", "", false (single dash, 2+ chars)
|
||||
// "--v" → "", "", false (double dash, 1 char)
|
||||
// "hello" → "", "", false (not a flag)
|
||||
func ParseFlag(arg string) (key, value string, valid bool) {
|
||||
if strings.HasPrefix(arg, "--") {
|
||||
// Long flag: must be 2+ chars
|
||||
rest := strings.TrimPrefix(arg, "--")
|
||||
parts := strings.SplitN(rest, "=", 2)
|
||||
name := parts[0]
|
||||
if utf8.RuneCountInString(name) < 2 {
|
||||
return "", "", false
|
||||
}
|
||||
if len(parts) == 2 {
|
||||
return name, parts[1], true
|
||||
}
|
||||
return name, "", true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(arg, "-") {
|
||||
// Short flag: must be exactly 1 char (rune)
|
||||
rest := strings.TrimPrefix(arg, "-")
|
||||
parts := strings.SplitN(rest, "=", 2)
|
||||
name := parts[0]
|
||||
if utf8.RuneCountInString(name) != 1 {
|
||||
return "", "", false
|
||||
}
|
||||
if len(parts) == 2 {
|
||||
return name, parts[1], true
|
||||
}
|
||||
return name, "", true
|
||||
}
|
||||
|
||||
return "", "", false
|
||||
}
|
||||
78
tests/utils_test.go
Normal file
78
tests/utils_test.go
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
package core_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "forge.lthn.ai/core/go/pkg/core"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// --- FilterArgs ---
|
||||
|
||||
func TestFilterArgs_Good(t *testing.T) {
|
||||
args := []string{"deploy", "", "to", "-test.v", "homelab", "-test.paniconexit0"}
|
||||
clean := FilterArgs(args)
|
||||
assert.Equal(t, []string{"deploy", "to", "homelab"}, clean)
|
||||
}
|
||||
|
||||
func TestFilterArgs_Empty_Good(t *testing.T) {
|
||||
clean := FilterArgs(nil)
|
||||
assert.Nil(t, clean)
|
||||
}
|
||||
|
||||
// --- ParseFlag ---
|
||||
|
||||
func TestParseFlag_ShortValid_Good(t *testing.T) {
|
||||
// Single letter
|
||||
k, v, ok := ParseFlag("-v")
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "v", k)
|
||||
assert.Equal(t, "", v)
|
||||
|
||||
// Single emoji
|
||||
k, v, ok = ParseFlag("-🔥")
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "🔥", k)
|
||||
assert.Equal(t, "", v)
|
||||
|
||||
// Short with value
|
||||
k, v, ok = ParseFlag("-p=8080")
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "p", k)
|
||||
assert.Equal(t, "8080", v)
|
||||
}
|
||||
|
||||
func TestParseFlag_ShortInvalid_Bad(t *testing.T) {
|
||||
// Multiple chars with single dash — invalid
|
||||
_, _, ok := ParseFlag("-verbose")
|
||||
assert.False(t, ok)
|
||||
|
||||
_, _, ok = ParseFlag("-port")
|
||||
assert.False(t, ok)
|
||||
}
|
||||
|
||||
func TestParseFlag_LongValid_Good(t *testing.T) {
|
||||
k, v, ok := ParseFlag("--verbose")
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "verbose", k)
|
||||
assert.Equal(t, "", v)
|
||||
|
||||
k, v, ok = ParseFlag("--port=8080")
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "port", k)
|
||||
assert.Equal(t, "8080", v)
|
||||
}
|
||||
|
||||
func TestParseFlag_LongInvalid_Bad(t *testing.T) {
|
||||
// Single char with double dash — invalid
|
||||
_, _, ok := ParseFlag("--v")
|
||||
assert.False(t, ok)
|
||||
}
|
||||
|
||||
func TestParseFlag_NotAFlag_Bad(t *testing.T) {
|
||||
_, _, ok := ParseFlag("hello")
|
||||
assert.False(t, ok)
|
||||
|
||||
_, _, ok = ParseFlag("")
|
||||
assert.False(t, ok)
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue