diff options
| author | Paul Buetow <paul@buetow.org> | 2026-06-25 17:38:03 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-06-25 17:38:03 +0300 |
| commit | 6ba890330df991eb12cd313a42cf2704f3c30227 (patch) | |
| tree | 52f0c28ed49bbb74a535822001acb2e6914e1f65 /internal/task/exec.go | |
| parent | c8c3307e9e636406491572d9b8dda3a84929613d (diff) | |
Refactor task package layout for 4r0
Diffstat (limited to 'internal/task/exec.go')
| -rw-r--r-- | internal/task/exec.go | 84 |
1 files changed, 84 insertions, 0 deletions
diff --git a/internal/task/exec.go b/internal/task/exec.go new file mode 100644 index 0000000..9ef84f2 --- /dev/null +++ b/internal/task/exec.go @@ -0,0 +1,84 @@ +package task + +import ( + "bytes" + "context" + "fmt" + "os/exec" + "strings" + + "github.com/google/shlex" +) + +func run(args ...string) error { + return runContext(context.Background(), args...) +} + +func runContext(ctx context.Context, args ...string) error { + _, err := RunArgs(ctx, args) + return err +} + +// RunLine splits line using shell-word rules and runs the resulting task +// arguments. A leading "task" token is ignored so callers may accept either +// "add foo" or "task add foo" from user input. +func RunLine(ctx context.Context, line string) (RunResult, error) { + fields, err := shlex.Split(line) + if err != nil { + return RunResult{}, err + } + if len(fields) > 0 && fields[0] == "task" { + fields = fields[1:] + } + return RunArgs(ctx, fields) +} + +// RunShellLine runs a user-entered task command in non-interactive mode. It +// avoids Taskwarrior's recurring-task prompt by applying the same behavior as +// answering "no": modify only the addressed recurrence. +func RunShellLine(ctx context.Context, line string) (RunResult, error) { + fields, err := shlex.Split(line) + if err != nil { + return RunResult{}, err + } + if len(fields) > 0 && fields[0] == "task" { + fields = fields[1:] + } + fields = append([]string{"rc.recurrence.confirmation=no"}, fields...) + return RunArgs(ctx, fields) +} + +// RunArgs runs "task" with args and captures stdout and stderr. +func RunArgs(ctx context.Context, args []string) (RunResult, error) { + copied := append([]string(nil), args...) + result := RunResult{Args: copied} + if len(copied) == 0 { + return result, fmt.Errorf("empty task command") + } + + if dbg.writer != nil { + if _, err := fmt.Fprintln(dbg.writer, "task "+strings.Join(copied, " ")); err != nil { + return result, fmt.Errorf("write debug log: %w", err) + } + } + + cmd := exec.CommandContext(ctx, "task", copied...) + configureCommandContext(cmd) + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + err := cmd.Run() + result.Stdout = stdout.String() + result.Stderr = stderr.String() + if err != nil { + if ctxErr := ctx.Err(); ctxErr != nil { + return result, fmt.Errorf("task command: %w", ctxErr) + } + if strings.TrimSpace(result.Stderr) != "" { + return result, fmt.Errorf("%w: %s", err, strings.TrimSpace(result.Stderr)) + } + return result, err + } + return result, nil +} |
