* feat(cli): improve agent ergonomics * fix(cli): address code review findings - Fix nil pointer dereference in confirmDestructive when flags is nil - Deduplicate dry-run logic by delegating to dryRunExit - Remove deprecated net.Error.Temporary() call (dead since Go 1.18) - Add unit tests for resolveTasklistID and resolveCalendarID Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve PR #201 conflicts and follow-ups (#201) (thanks @salmonumbrella) * fix: resolve rebase fallout for PR #201 landing (#201) (thanks @salmonumbrella) --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Peter Steinberger <steipete@gmail.com>
44 lines
997 B
Go
44 lines
997 B
Go
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
const emptyResultsExitCode = 3
|
|
|
|
func failEmptyExit(failEmpty bool) error {
|
|
if !failEmpty {
|
|
return nil
|
|
}
|
|
return &ExitError{Code: emptyResultsExitCode, Err: nil}
|
|
}
|
|
|
|
// collectAllPages keeps calling fetch until it returns an empty next page token.
|
|
// It guards against pagination loops by tracking seen page tokens.
|
|
func collectAllPages[T any](startPageToken string, fetch func(pageToken string) ([]T, string, error)) ([]T, error) {
|
|
pageToken := strings.TrimSpace(startPageToken)
|
|
seen := map[string]bool{}
|
|
|
|
var out []T
|
|
for i := 0; i < 10_000; i++ {
|
|
if seen[pageToken] {
|
|
return nil, fmt.Errorf("pagination loop: repeated page token %q", pageToken)
|
|
}
|
|
seen[pageToken] = true
|
|
|
|
items, next, err := fetch(pageToken)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, items...)
|
|
|
|
next = strings.TrimSpace(next)
|
|
if next == "" {
|
|
return out, nil
|
|
}
|
|
pageToken = next
|
|
}
|
|
return nil, fmt.Errorf("pagination exceeded max pages")
|
|
}
|