summaryrefslogtreecommitdiff
path: root/internal/askcli/dispatch.go
blob: c2c7aab1f85be8af42982674c74f44a4d1fc4a23 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package askcli

import (
	"context"
	"fmt"
	"io"
)

// Runner performs CLI work that would otherwise be handled by ask itself.
//
// The interface is implemented by the executor that ultimately proxies commands to Taskwarrior.
type Runner interface {
	Run(ctx context.Context, args []string, stdin io.Reader, stdout, stderr io.Writer) (int, error)
}

// Dispatcher translates CLI arguments into concrete subcommands and presents the output.
type Dispatcher struct {
	runner     Runner
	jsonOutput bool
}

// NewDispatcher creates a Dispatcher backed by the provided Runner or a default executor when nil.
func NewDispatcher(runner Runner) *Dispatcher {
	if runner == nil {
		e := NewExecutor("ask")
		runner = &e
	}
	return &Dispatcher{runner: runner}
}

func parseGlobalFlags(args []string) ([]string, bool) {
	var filtered []string
	var jsonOutput bool
	for _, arg := range args {
		if arg == "--json" {
			jsonOutput = true
			continue
		}
		filtered = append(filtered, arg)
	}
	return filtered, jsonOutput
}

// Dispatch parses CLI arguments, handles global flags, and routes the request to the matching subcommand.
func (d *Dispatcher) Dispatch(ctx context.Context, args []string, stdin io.Reader, stdout, stderr io.Writer) (int, error) {
	args, jsonOutput := parseGlobalFlags(args)
	d.jsonOutput = jsonOutput

	if len(args) == 0 {
		args = []string{"list"}
	}
	subcommand := args[0]
	entry, ok := commandRegistry.get(subcommand)
	if !ok {
		return d.unknownCommand(stderr, subcommand)
	}
	return entry.handler(d, ctx, args, stdin, stdout, stderr)
}

func (d *Dispatcher) help(w io.Writer) (int, error) {
	_, _ = io.WriteString(w, "ask - task management CLI\n")
	_, _ = io.WriteString(w, "\nSubcommands:\n")
	_, _ = io.WriteString(w, "  ask add [mods...] [depends:<id|uuid>,...] <description...> Create a new task and print its ID\n")
	_, _ = io.WriteString(w, "  ask list [filters]           List active tasks (default)\n")
	_, _ = io.WriteString(w, "  ask ready                   List READY tasks (not blocked)\n")
	_, _ = io.WriteString(w, "  ask all [filters]            List all tasks including completed/deleted\n")
	_, _ = io.WriteString(w, "  ask info [id|uuid]            Show task details or current started task\n")
	_, _ = io.WriteString(w, "  ask annotate <id|uuid> \"note\" Add annotation to task\n")
	_, _ = io.WriteString(w, "  ask start <id|uuid>           Start working on task\n")
	_, _ = io.WriteString(w, "  ask stop <id|uuid>            Stop work on a task\n")
	_, _ = io.WriteString(w, "  ask done <id|uuid>            Mark task complete\n")
	_, _ = io.WriteString(w, "  ask priority <id|uuid> <P>    Set priority (H/M/L)\n")
	_, _ = io.WriteString(w, "  ask tag <id|uuid> +/-<tag>    Add or remove tag\n")
	_, _ = io.WriteString(w, "  ask dep add <id|uuid> <dep>   Add dependency\n")
	_, _ = io.WriteString(w, "  ask dep rm <id|uuid> <dep>    Remove dependency\n")
	_, _ = io.WriteString(w, "  ask dep list <id|uuid>        List dependencies\n")
	_, _ = io.WriteString(w, "  ask urgency                   List tasks sorted by urgency\n")
	_, _ = io.WriteString(w, "  ask modify <id|uuid> <args...> Modify task fields\n")
	_, _ = io.WriteString(w, "  ask denotate <id|uuid> \"text\" Remove annotation\n")
	_, _ = io.WriteString(w, "  ask delete <id|uuid>          Delete a task\n")
	_, _ = io.WriteString(w, "  ask fish                      Emit Fish shell completion script\n")
	return 0, nil
}

func (d *Dispatcher) unknownCommand(w io.Writer, subcommand string) (int, error) {
	fmt.Fprintf(w, "ask: unknown subcommand %q\n", subcommand)
	return 1, nil
}