2026-03-20 12:15:57 +00:00
|
|
|
// SPDX-License-Identifier: EUPL-1.2
|
|
|
|
|
|
|
|
|
|
// Utility functions for the Core framework.
|
feat: string.go — core string primitives, same pattern as array.go
HasPrefix, HasSuffix, TrimPrefix, TrimSuffix, Contains, Split, SplitN,
StringJoin, Replace, Lower, Upper, Trim, RuneCount.
utils.go and command.go now use string.go helpers — zero direct
strings import in either file.
234 tests, 79.8% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 12:29:15 +00:00
|
|
|
// Built on core string.go primitives.
|
2026-03-20 12:15:57 +00:00
|
|
|
|
|
|
|
|
package core
|
|
|
|
|
|
|
|
|
|
import (
|
2026-03-20 12:19:11 +00:00
|
|
|
"fmt"
|
|
|
|
|
"io"
|
|
|
|
|
"os"
|
2026-03-20 12:15:57 +00:00
|
|
|
)
|
|
|
|
|
|
2026-03-20 12:20:36 +00:00
|
|
|
// Print writes a formatted line to a writer, defaulting to os.Stdout.
|
2026-03-20 12:19:11 +00:00
|
|
|
//
|
2026-03-20 12:20:36 +00:00
|
|
|
// core.Print(nil, "hello %s", "world") // → stdout
|
|
|
|
|
// core.Print(w, "port: %d", 8080) // → w
|
|
|
|
|
func Print(w io.Writer, format string, args ...any) {
|
2026-03-20 12:19:11 +00:00
|
|
|
if w == nil {
|
|
|
|
|
w = os.Stdout
|
|
|
|
|
}
|
|
|
|
|
fmt.Fprintf(w, format+"\n", args...)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-20 12:23:05 +00:00
|
|
|
// JoinPath joins string segments into a path with "/" separator.
|
|
|
|
|
//
|
|
|
|
|
// core.JoinPath("deploy", "to", "homelab") // → "deploy/to/homelab"
|
|
|
|
|
func JoinPath(segments ...string) string {
|
2026-03-20 12:42:10 +00:00
|
|
|
return Join("/", segments...)
|
2026-03-20 12:23:05 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-20 12:24:39 +00:00
|
|
|
// IsFlag returns true if the argument starts with a dash.
|
|
|
|
|
//
|
|
|
|
|
// core.IsFlag("--verbose") // true
|
|
|
|
|
// core.IsFlag("-v") // true
|
|
|
|
|
// core.IsFlag("deploy") // false
|
|
|
|
|
func IsFlag(arg string) bool {
|
feat: string.go — core string primitives, same pattern as array.go
HasPrefix, HasSuffix, TrimPrefix, TrimSuffix, Contains, Split, SplitN,
StringJoin, Replace, Lower, Upper, Trim, RuneCount.
utils.go and command.go now use string.go helpers — zero direct
strings import in either file.
234 tests, 79.8% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 12:29:15 +00:00
|
|
|
return HasPrefix(arg, "-")
|
2026-03-20 12:24:39 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-20 12:50:59 +00:00
|
|
|
// Arg extracts a value from variadic args at the given index.
|
|
|
|
|
// Type-checks and delegates to the appropriate typed extractor.
|
|
|
|
|
// Returns the typed value — string for strings, int for ints, etc.
|
2026-03-20 12:46:52 +00:00
|
|
|
//
|
2026-03-20 12:50:59 +00:00
|
|
|
// path := core.Arg(0, args...).(string)
|
|
|
|
|
// name := core.Arg(0, "hello", 42) // returns "hello"
|
|
|
|
|
func Arg(index int, args ...any) any {
|
2026-03-20 12:46:52 +00:00
|
|
|
if index >= len(args) {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2026-03-20 12:50:59 +00:00
|
|
|
v := args[index]
|
|
|
|
|
switch v.(type) {
|
|
|
|
|
case string:
|
|
|
|
|
return ArgString(index, args...)
|
|
|
|
|
case int:
|
|
|
|
|
return ArgInt(index, args...)
|
|
|
|
|
case bool:
|
|
|
|
|
return ArgBool(index, args...)
|
|
|
|
|
default:
|
|
|
|
|
return v
|
|
|
|
|
}
|
2026-03-20 12:46:52 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-20 12:50:59 +00:00
|
|
|
// ArgString extracts a string at the given index.
|
2026-03-20 12:44:57 +00:00
|
|
|
//
|
2026-03-20 12:50:59 +00:00
|
|
|
// name := core.ArgString(0, args...)
|
|
|
|
|
func ArgString(index int, args ...any) string {
|
|
|
|
|
if index >= len(args) {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
s, ok := args[index].(string)
|
|
|
|
|
if !ok {
|
2026-03-20 12:44:57 +00:00
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-20 12:50:59 +00:00
|
|
|
// ArgInt extracts an int at the given index.
|
2026-03-20 12:46:52 +00:00
|
|
|
//
|
2026-03-20 12:50:59 +00:00
|
|
|
// port := core.ArgInt(1, args...)
|
|
|
|
|
func ArgInt(index int, args ...any) int {
|
|
|
|
|
if index >= len(args) {
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
i, ok := args[index].(int)
|
|
|
|
|
if !ok {
|
2026-03-20 12:46:52 +00:00
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-20 12:50:59 +00:00
|
|
|
// ArgBool extracts a bool at the given index.
|
2026-03-20 12:46:52 +00:00
|
|
|
//
|
2026-03-20 12:50:59 +00:00
|
|
|
// debug := core.ArgBool(2, args...)
|
|
|
|
|
func ArgBool(index int, args ...any) bool {
|
|
|
|
|
if index >= len(args) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
b, ok := args[index].(bool)
|
|
|
|
|
if !ok {
|
2026-03-20 12:46:52 +00:00
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
return b
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-20 12:15:57 +00:00
|
|
|
// 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 {
|
feat: string.go — core string primitives, same pattern as array.go
HasPrefix, HasSuffix, TrimPrefix, TrimSuffix, Contains, Split, SplitN,
StringJoin, Replace, Lower, Upper, Trim, RuneCount.
utils.go and command.go now use string.go helpers — zero direct
strings import in either file.
234 tests, 79.8% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 12:29:15 +00:00
|
|
|
if a == "" || HasPrefix(a, "-test.") {
|
2026-03-20 12:15:57 +00:00
|
|
|
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) {
|
feat: string.go — core string primitives, same pattern as array.go
HasPrefix, HasSuffix, TrimPrefix, TrimSuffix, Contains, Split, SplitN,
StringJoin, Replace, Lower, Upper, Trim, RuneCount.
utils.go and command.go now use string.go helpers — zero direct
strings import in either file.
234 tests, 79.8% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 12:29:15 +00:00
|
|
|
if HasPrefix(arg, "--") {
|
|
|
|
|
rest := TrimPrefix(arg, "--")
|
|
|
|
|
parts := SplitN(rest, "=", 2)
|
2026-03-20 12:15:57 +00:00
|
|
|
name := parts[0]
|
feat: string.go — core string primitives, same pattern as array.go
HasPrefix, HasSuffix, TrimPrefix, TrimSuffix, Contains, Split, SplitN,
StringJoin, Replace, Lower, Upper, Trim, RuneCount.
utils.go and command.go now use string.go helpers — zero direct
strings import in either file.
234 tests, 79.8% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 12:29:15 +00:00
|
|
|
if RuneCount(name) < 2 {
|
2026-03-20 12:15:57 +00:00
|
|
|
return "", "", false
|
|
|
|
|
}
|
|
|
|
|
if len(parts) == 2 {
|
|
|
|
|
return name, parts[1], true
|
|
|
|
|
}
|
|
|
|
|
return name, "", true
|
|
|
|
|
}
|
|
|
|
|
|
feat: string.go — core string primitives, same pattern as array.go
HasPrefix, HasSuffix, TrimPrefix, TrimSuffix, Contains, Split, SplitN,
StringJoin, Replace, Lower, Upper, Trim, RuneCount.
utils.go and command.go now use string.go helpers — zero direct
strings import in either file.
234 tests, 79.8% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 12:29:15 +00:00
|
|
|
if HasPrefix(arg, "-") {
|
|
|
|
|
rest := TrimPrefix(arg, "-")
|
|
|
|
|
parts := SplitN(rest, "=", 2)
|
2026-03-20 12:15:57 +00:00
|
|
|
name := parts[0]
|
feat: string.go — core string primitives, same pattern as array.go
HasPrefix, HasSuffix, TrimPrefix, TrimSuffix, Contains, Split, SplitN,
StringJoin, Replace, Lower, Upper, Trim, RuneCount.
utils.go and command.go now use string.go helpers — zero direct
strings import in either file.
234 tests, 79.8% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 12:29:15 +00:00
|
|
|
if RuneCount(name) != 1 {
|
2026-03-20 12:15:57 +00:00
|
|
|
return "", "", false
|
|
|
|
|
}
|
|
|
|
|
if len(parts) == 2 {
|
|
|
|
|
return name, parts[1], true
|
|
|
|
|
}
|
|
|
|
|
return name, "", true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return "", "", false
|
|
|
|
|
}
|