1package main
2
3import (
4 "encoding/gob"
5 "log"
6 "os"
7 "sync"
8 "time"
9)
10
11type CommitStat struct {
12 Additions int
13 Deletions int
14}
15
16type RepoMetadata struct {
17 Branches []string
18 Tags []string
19 ReadmeName string
20 LicenseName string
21 TotalCommits int
22 Version int
23}
24
25const CurrentMetadataVersion = 1
26
27type LangCacheKey struct {
28 RepoName string
29 CommitHash string
30}
31
32var (
33 commitStatsCache sync.Map // string -> CommitStat
34 repoMetadataCache sync.Map // string -> RepoMetadata
35 langCache sync.Map // LangCacheKey -> []LanguageStat
36 saveChan = make(chan struct{}, 1)
37)
38
39const cacheFile = "bbgit.cache"
40
41func NotifySave() {
42 select {
43 case saveChan <- struct{}{}:
44 default:
45 }
46}
47
48func StartSaver() {
49 go func() {
50 for range saveChan {
51 // Throttle saves to once per second
52 time.Sleep(1 * time.Second)
53 // Drain any additional signals during sleep
54 for len(saveChan) > 0 {
55 <-saveChan
56 }
57 if err := SaveCache(); err != nil {
58 log.Printf("Error saving cache: %v", err)
59 }
60 }
61 }()
62}
63
64type cacheContainer struct {
65 CommitStats map[string]CommitStat
66 RepoMetadata map[string]RepoMetadata
67 LangStats map[LangCacheKey][]LanguageStat
68}
69
70func SaveCache() error {
71 container := cacheContainer{
72 CommitStats: make(map[string]CommitStat),
73 RepoMetadata: make(map[string]RepoMetadata),
74 LangStats: make(map[LangCacheKey][]LanguageStat),
75 }
76
77 commitStatsCache.Range(func(k, v interface{}) bool {
78 container.CommitStats[k.(string)] = v.(CommitStat)
79 return true
80 })
81
82 repoMetadataCache.Range(func(k, v interface{}) bool {
83 container.RepoMetadata[k.(string)] = v.(RepoMetadata)
84 return true
85 })
86
87 langCache.Range(func(k, v interface{}) bool {
88 container.LangStats[k.(LangCacheKey)] = v.([]LanguageStat)
89 return true
90 })
91
92 f, err := os.Create(cacheFile)
93 if err != nil {
94 return err
95 }
96 defer f.Close()
97
98 if err := gob.NewEncoder(f).Encode(container); err != nil {
99 return err
100 }
101
102 log.Printf("OPS Cache saved to %s", cacheFile)
103 return nil
104}
105
106func LoadCache() {
107 f, err := os.Open(cacheFile)
108 if err != nil {
109 if !os.IsNotExist(err) {
110 log.Printf("Error opening cache file: %v", err)
111 }
112 return
113 }
114 defer f.Close()
115
116 var container cacheContainer
117 if err := gob.NewDecoder(f).Decode(&container); err != nil {
118 log.Printf("Error decoding cache file: %v", err)
119 return
120 }
121
122 for k, v := range container.CommitStats {
123 commitStatsCache.Store(k, v)
124 }
125 for k, v := range container.RepoMetadata {
126 repoMetadataCache.Store(k, v)
127 }
128 for k, v := range container.LangStats {
129 langCache.Store(k, v)
130 }
131
132 log.Printf("Loaded cache from %s", cacheFile)
133}