11package main
22
33import (
4+ "context"
5+ "encoding/json"
6+ "os"
7+ "os/exec"
8+ "path/filepath"
9+ "strings"
10+ "time"
11+
412 "github.com/julianknutsen/wasteland/internal/commons"
513 "github.com/spf13/cobra"
614)
715
16+ const completionCacheTTL = 5 * time .Second
17+
818// completeWantedIDs returns a ValidArgsFunction that completes wanted IDs,
919// optionally filtered by status (e.g. "open", "claimed", "in_review").
1020func completeWantedIDs (statusFilter string ) func (* cobra.Command , []string , string ) ([]string , cobra.ShellCompDirective ) {
@@ -16,10 +26,12 @@ func completeWantedIDs(statusFilter string) func(*cobra.Command, []string, strin
1626 if err != nil {
1727 return nil , cobra .ShellCompDirectiveNoFileComp
1828 }
19- ids , err := commons . ListWantedIDs ( cfg . LocalDir , statusFilter )
20- if err != nil {
21- return nil , cobra .ShellCompDirectiveNoFileComp
29+ cacheKey := "wanted-" + statusFilter
30+ if cached := readCompletionCache ( cacheKey ); cached != nil {
31+ return cached , cobra .ShellCompDirectiveNoFileComp
2232 }
33+ ids := listWantedIDsWithTimeout (cfg .LocalDir , statusFilter )
34+ writeCompletionCache (cacheKey , ids )
2335 return ids , cobra .ShellCompDirectiveNoFileComp
2436 }
2537}
@@ -33,6 +45,104 @@ func completeBranchNames(cmd *cobra.Command, args []string, _ string) ([]string,
3345 if err != nil {
3446 return nil , cobra .ShellCompDirectiveNoFileComp
3547 }
36- branches , _ := commons .ListBranches (cfg .LocalDir , "wl/" )
48+ cacheKey := "branches"
49+ if cached := readCompletionCache (cacheKey ); cached != nil {
50+ return cached , cobra .ShellCompDirectiveNoFileComp
51+ }
52+ branches := listBranchesWithTimeout (cfg .LocalDir )
53+ writeCompletionCache (cacheKey , branches )
3754 return branches , cobra .ShellCompDirectiveNoFileComp
3855}
56+
57+ // listWantedIDsWithTimeout queries wanted IDs with a 2-second timeout.
58+ func listWantedIDsWithTimeout (dbDir , statusFilter string ) []string {
59+ query := "SELECT id FROM wanted"
60+ if statusFilter != "" {
61+ query += " WHERE status = '" + commons .EscapeSQL (statusFilter ) + "'"
62+ }
63+ query += " ORDER BY created_at DESC LIMIT 50"
64+ out := doltQueryWithTimeout (dbDir , query , 2 * time .Second )
65+ if out == "" {
66+ return nil
67+ }
68+ lines := strings .Split (strings .TrimSpace (out ), "\n " )
69+ if len (lines ) < 2 {
70+ return nil
71+ }
72+ var ids []string
73+ for _ , line := range lines [1 :] {
74+ id := strings .TrimSpace (line )
75+ if id != "" {
76+ ids = append (ids , id )
77+ }
78+ }
79+ return ids
80+ }
81+
82+ // listBranchesWithTimeout queries wl/* branches with a 2-second timeout.
83+ func listBranchesWithTimeout (dbDir string ) []string {
84+ query := "SELECT name FROM dolt_branches WHERE name LIKE 'wl/%' ORDER BY name"
85+ out := doltQueryWithTimeout (dbDir , query , 2 * time .Second )
86+ if out == "" {
87+ return nil
88+ }
89+ lines := strings .Split (strings .TrimSpace (out ), "\n " )
90+ if len (lines ) < 2 {
91+ return nil
92+ }
93+ var branches []string
94+ for _ , line := range lines [1 :] {
95+ name := strings .TrimSpace (line )
96+ if name != "" {
97+ branches = append (branches , name )
98+ }
99+ }
100+ return branches
101+ }
102+
103+ // doltQueryWithTimeout runs a dolt SQL query with a strict timeout.
104+ func doltQueryWithTimeout (dbDir , query string , timeout time.Duration ) string {
105+ ctx , cancel := context .WithTimeout (context .Background (), timeout )
106+ defer cancel ()
107+ cmd := exec .CommandContext (ctx , "dolt" , "sql" , "-r" , "csv" , "-q" , query )
108+ cmd .Dir = dbDir
109+ output , err := cmd .CombinedOutput ()
110+ if err != nil {
111+ return ""
112+ }
113+ return string (output )
114+ }
115+
116+ // completionCacheDir returns the directory for completion cache files.
117+ func completionCacheDir () string {
118+ return filepath .Join (os .TempDir (), "wl-completion-cache" )
119+ }
120+
121+ // readCompletionCache returns cached completions if the cache is fresh.
122+ func readCompletionCache (key string ) []string {
123+ path := filepath .Join (completionCacheDir (), key + ".json" )
124+ info , err := os .Stat (path )
125+ if err != nil || time .Since (info .ModTime ()) > completionCacheTTL {
126+ return nil
127+ }
128+ data , err := os .ReadFile (path )
129+ if err != nil {
130+ return nil
131+ }
132+ var items []string
133+ if err := json .Unmarshal (data , & items ); err != nil {
134+ return nil
135+ }
136+ return items
137+ }
138+
139+ // writeCompletionCache writes completions to the cache.
140+ func writeCompletionCache (key string , items []string ) {
141+ dir := completionCacheDir ()
142+ _ = os .MkdirAll (dir , 0o755 )
143+ data , err := json .Marshal (items )
144+ if err != nil {
145+ return
146+ }
147+ _ = os .WriteFile (filepath .Join (dir , key + ".json" ), data , 0o644 )
148+ }
0 commit comments