refactor: split monolithic main.go into modular structure

Refactored 800+ line main.go into clean, domain-separated files:
- main.go (96 lines) - Entry point and command router only
- types.go - Type definitions and data structures
- client.go - HTTP client and request handling
- cmd_kv.go - Key-value operations (get/put/delete)
- cmd_meta.go - Resource metadata commands
- cmd_user.go - User management commands
- cmd_profile.go - Profile management
- cmd_cluster.go - Cluster operations (members/health)
- cmd_system.go - System commands (connect/auth/help)
- utils.go - Shared utilities (parsing, colors, completion)

No functional changes, pure reorganization for maintainability.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-05 23:34:18 +03:00
parent fcd27dee97
commit 0c9e314d7c
10 changed files with 764 additions and 710 deletions

124
utils.go Normal file
View File

@@ -0,0 +1,124 @@
package main
import (
"fmt"
"strings"
"github.com/chzyer/readline"
"github.com/fatih/color"
)
// Color helpers
var (
cyan = color.New(color.FgCyan).SprintFunc()
green = color.New(color.FgGreen).SprintFunc()
yellow = color.New(color.FgYellow).SprintFunc()
red = color.New(color.FgRed).SprintFunc()
magenta = color.New(color.FgMagenta).SprintFunc()
)
// parseCommand parses command line respecting quotes
func parseCommand(line string) []string {
var parts []string
var current strings.Builder
var quoteChar rune = 0 // Use a rune to store the active quote character (' or ")
for _, ch := range line {
switch {
// If we see a quote character
case ch == '\'' || ch == '"':
if quoteChar == 0 {
// Not currently inside a quoted string, so start one
quoteChar = ch
} else if quoteChar == ch {
// Inside a quoted string and found the matching quote, so end it
quoteChar = 0
} else {
// Inside a quoted string but found the *other* type of quote, treat it as a literal character
current.WriteRune(ch)
}
// If we see a space or tab and are NOT inside a quoted string
case (ch == ' ' || ch == '\t') && quoteChar == 0:
if current.Len() > 0 {
parts = append(parts, current.String())
current.Reset()
}
// Any other character
default:
current.WriteRune(ch)
}
}
// Add the last part if it exists
if current.Len() > 0 {
parts = append(parts, current.String())
}
return parts
}
// printJSONError displays a user-friendly JSON parsing error with helpful hints
func printJSONError(input string, err error) {
fmt.Println(red("❌ Invalid JSON:"), err)
fmt.Println()
// Show what was received
if len(input) > 100 {
fmt.Println(yellow("Received:"), input[:100]+"...")
} else {
fmt.Println(yellow("Received:"), input)
}
fmt.Println()
// Provide helpful hints based on common errors
errMsg := err.Error()
if strings.Contains(errMsg, "looking for beginning of") ||
strings.Contains(errMsg, "invalid character") {
fmt.Println(cyan("💡 Tip:"), "JSON with spaces needs quotes:")
fmt.Println(green(" ✓ Correct:"), "put key '{\"hello\":\"world\"}'")
fmt.Println(green(" ✓ Correct:"), "put key '{\"a\":1,\"b\":2}'")
fmt.Println(green(" ✓ Compact:"), "put key {\"a\":1,\"b\":2} (no spaces)")
fmt.Println(red(" ✗ Wrong: "), "put key {\"a\": 1, \"b\": 2} (spaces without quotes)")
} else if strings.Contains(errMsg, "unexpected end of JSON") {
fmt.Println(cyan("💡 Tip:"), "JSON appears incomplete or cut off")
fmt.Println(green(" Check:"), "Are all brackets/braces matched? {}, []")
fmt.Println(green(" Check:"), "Did spaces split your JSON into multiple arguments?")
} else {
fmt.Println(cyan("💡 Tip:"), "Wrap JSON in quotes for complex values:")
fmt.Println(green(" Single quotes:"), "put key '{\"your\":\"json\"}'")
fmt.Println(green(" Double quotes:"), "put key \"{\\\"your\\\":\\\"json\\\"}\"")
}
}
// Auto-completion setup
var completer = readline.NewPrefixCompleter(
readline.PcItem("connect"),
readline.PcItem("auth"),
readline.PcItem("profile",
readline.PcItem("add"),
readline.PcItem("use"),
readline.PcItem("remove"),
),
readline.PcItem("get"),
readline.PcItem("put"),
readline.PcItem("delete"),
readline.PcItem("meta",
readline.PcItem("get"),
readline.PcItem("set",
readline.PcItem("--owner"),
readline.PcItem("--group"),
readline.PcItem("--permissions"),
),
),
readline.PcItem("members"),
readline.PcItem("health"),
readline.PcItem("user",
readline.PcItem("get"),
readline.PcItem("create"),
),
readline.PcItem("help"),
readline.PcItem("exit"),
readline.PcItem("quit"),
readline.PcItem("clear"),
)