From b67069c110c210b05507fca839d45b43431f5e86 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Thu, 26 Mar 2026 23:45:09 +0200 Subject: askcli: resolve aliases for selector task 0b9480fe-ec1b-4c0e-a8b0-88f1f08b56d3 --- internal/askcli/task_selector.go | 119 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 internal/askcli/task_selector.go (limited to 'internal/askcli/task_selector.go') diff --git a/internal/askcli/task_selector.go b/internal/askcli/task_selector.go new file mode 100644 index 0000000..7702153 --- /dev/null +++ b/internal/askcli/task_selector.go @@ -0,0 +1,119 @@ +package askcli + +import ( + "context" + "fmt" + "io" + "strings" +) + +type resolvedTaskSelector struct { + Input string + UUID string + Alias string + UsedAlias bool +} + +func (d Dispatcher) resolveTaskSelector(ctx context.Context, selector string, stderr io.Writer) (resolvedTaskSelector, []TaskExport, int, error) { + normalized, requiresLookup, err := normalizeTaskSelectorInput(selector) + if err != nil { + return resolvedTaskSelector{}, nil, 1, err + } + + resolved, err := resolveTaskSelectorFromCache(normalized, requiresLookup) + if err != nil { + return resolvedTaskSelector{}, nil, 1, err + } + + tasks, code, err := d.exportTasks(ctx, []string{"uuid:" + resolved.UUID, "export"}, stderr) + if err != nil { + if resolved.UsedAlias && strings.Contains(err.Error(), "task not found") { + return resolvedTaskSelector{}, nil, 1, fmt.Errorf("alias %q is stale: task %s was not found", selector, resolved.UUID) + } + return resolvedTaskSelector{}, nil, code, err + } + + aliases, err := ensureTaskAliases(tasks) + if err != nil { + return resolvedTaskSelector{}, nil, 1, err + } + if alias, ok := aliases[resolved.UUID]; ok { + resolved.Alias = alias + } + return resolved, tasks, 0, nil +} + +func normalizeTaskSelectorInput(selector string) (string, bool, error) { + normalized := NormalizeUUID(selector) + if selector != normalized && IsNumericID(normalized) { + return "", false, fmt.Errorf(strings.TrimSpace(RejectNumericID())) + } + return normalized, selector == normalized, nil +} + +func resolveTaskSelectorFromCache(selector string, allowAlias bool) (resolvedTaskSelector, error) { + resolved := resolvedTaskSelector{Input: selector, UUID: selector} + if !allowAlias || !looksLikeTaskAlias(selector) { + return resolved, nil + } + + cache, path, err := loadTaskAliasCache() + if err != nil { + return resolvedTaskSelector{}, err + } + + now := nowTaskAliasCache().UTC() + changed := cache.prune(now) + uuidFromAlias, aliasFound, aliasChanged := cache.lookupUUIDByAlias(selector, now) + changed = changed || aliasChanged + aliasForUUID, uuidFound, uuidChanged := cache.lookupAliasByUUID(selector, now) + changed = changed || uuidChanged + + switch { + case aliasFound && uuidFound && uuidFromAlias != selector: + if changed { + if err := cache.save(path); err != nil { + return resolvedTaskSelector{}, err + } + } + return resolvedTaskSelector{}, fmt.Errorf("task selector %q is ambiguous: it matches alias for %s and UUID %s; use uuid:%s to force UUID", selector, uuidFromAlias, selector, selector) + case aliasFound: + if changed { + if err := cache.save(path); err != nil { + return resolvedTaskSelector{}, err + } + } + return resolvedTaskSelector{ + Input: selector, + UUID: uuidFromAlias, + Alias: selector, + UsedAlias: true, + }, nil + case uuidFound: + if changed { + if err := cache.save(path); err != nil { + return resolvedTaskSelector{}, err + } + } + return resolvedTaskSelector{ + Input: selector, + UUID: selector, + Alias: aliasForUUID, + }, nil + default: + if IsNumericID(selector) { + return resolvedTaskSelector{}, fmt.Errorf(strings.TrimSpace(RejectNumericID())) + } + if changed { + if err := cache.save(path); err != nil { + return resolvedTaskSelector{}, err + } + } + return resolvedTaskSelector{}, fmt.Errorf("task selector %q did not match a known alias; use uuid:%s to force UUID", selector, selector) + } +} + +func looksLikeTaskAlias(selector string) bool { + _, ok := decodeTaskAliasID(selector) + return ok && !strings.Contains(selector, "-") +} -- cgit v1.2.3