summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-24 14:13:58 +0300
committerPaul Buetow <paul@buetow.org>2026-05-24 14:13:58 +0300
commita8bf293f048b91c912c77768b6a7859f58739e9c (patch)
tree05bcf03cd870ab104349bca36fbbb1f2eec7b28a
parentc38e792622bd392b09e6ed95a1242015b26f85a2 (diff)
refactor: split runCommand() into focused helpers (mj)
Extract runCommand() in cmd/gt/main.go into separate helpers following SRP: - parseFlags(): extract --log flag parsing - dispatchInput(): route by arg count/content (version, stdin, args) - handleNoInput(): TTY→REPL or stdin→parse path - startREPL(): REPL launch with optional log file - tryParse(): RPN-first with percentage fallback runCommand() is now a 3-line orchestrator delegating to parseFlags() and dispatchInput(). No behavioral change; all tests pass.
-rw-r--r--cmd/gt/main.go100
1 files changed, 43 insertions, 57 deletions
diff --git a/cmd/gt/main.go b/cmd/gt/main.go
index cc4d3e2..85292fb 100644
--- a/cmd/gt/main.go
+++ b/cmd/gt/main.go
@@ -70,85 +70,71 @@ func main() {
}
}
-// runCommand processes command-line arguments and executes the appropriate action.
-//
-// It handles:
-// - --log <file>: Append REPL input/output to the specified log file
-// - No arguments: Start REPL mode if stdin is a TTY, otherwise read from stdin
-// - "version" argument: Return the version string
-// - Other arguments: Try RPN parsing first, then fall back to percentage calculation
+// runCommand orchestrates command-line argument processing and execution.
func runCommand(args []string) (string, error) {
- // Check for --log flag
- var logFile string
- var remainingArgs []string
+ logFile, args := parseFlags(args)
+ return dispatchInput(args, logFile)
+}
+// parseFlags extracts the --log <file> flag and returns it along with the remaining args.
+func parseFlags(args []string) (logFile string, remaining []string) {
for i := 0; i < len(args); i++ {
if args[i] == "--log" && i+1 < len(args) {
logFile = args[i+1]
- i++ // Skip the filename argument
+ i++
} else {
- remainingArgs = append(remainingArgs, args[i])
+ remaining = append(remaining, args[i])
}
}
+ return
+}
- // Update args to exclude --log flag
- args = remainingArgs
-
+// dispatchInput routes based on the number and content of remaining args.
+func dispatchInput(args []string, logFile string) (string, error) {
if len(args) < 2 {
- // No args provided - check if stdin is a TTY for REPL mode
- if isatty.IsTerminal(os.Stdin.Fd()) {
- if logFile != "" {
- if err := repl.RunREPLWithLog(logFile); err != nil {
- return "", err
- }
- } else {
- if err := runREPL(); err != nil {
- return "", err
- }
- }
- return "", nil
- }
- // Read from stdin (pipe or redirect)
- input, err := readStdin()
- if err != nil {
- return "", fmt.Errorf("failed to read stdin: %w", err)
- }
- input = strings.TrimSpace(input)
- if input == "" {
- printUsage()
- return "", fmt.Errorf("no input provided")
- }
- // Try RPN parsing first
- rpnResult, rpnErr := runRPN(input)
- if rpnErr == nil {
- return rpnResult, nil
- }
- // Fall back to percentage calculation
- result, err := perc.Parse(input)
- if err != nil {
- return "", fmt.Errorf("rpn fallback failed for input %q: %w", input, err)
- }
- return result, nil
+ return handleNoInput(logFile)
}
-
if args[1] == "version" {
return internal.Version, nil
}
-
input := strings.Join(args[1:], " ")
+ return tryParse(input)
+}
+
+// handleNoInput handles the no-argument case: REPL on TTY, or stdin read and parse.
+func handleNoInput(logFile string) (string, error) {
+ if isatty.IsTerminal(os.Stdin.Fd()) {
+ return startREPL(logFile)
+ }
+ input, err := readStdin()
+ if err != nil {
+ return "", fmt.Errorf("failed to read stdin: %w", err)
+ }
+ input = strings.TrimSpace(input)
+ if input == "" {
+ printUsage()
+ return "", fmt.Errorf("no input provided")
+ }
+ return tryParse(input)
+}
- // Try RPN parsing first (for bare RPN expressions like "3 4 +")
- rpnResult, rpnErr := runRPN(input)
- if rpnErr == nil {
- return rpnResult, nil
+// startREPL launches the REPL, with optional logging when logFile is non-empty.
+func startREPL(logFile string) (string, error) {
+ if logFile != "" {
+ return "", repl.RunREPLWithLog(logFile)
}
+ return "", runREPL()
+}
- // Fall back to percentage calculation
+// tryParse attempts RPN evaluation first, falling back to percentage calculation.
+func tryParse(input string) (string, error) {
+ if result, err := runRPN(input); err == nil {
+ return result, nil
+ }
result, err := perc.Parse(input)
if err != nil {
return "", fmt.Errorf("rpn fallback failed for input %q: %w", input, err)
}
-
return result, nil
}