-
Notifications
You must be signed in to change notification settings - Fork 1
/
db.go
122 lines (110 loc) · 2.48 KB
/
db.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"sync"
"github.com/google/renameio/v2"
)
func readLinks(rdr io.Reader) ([]Link, error) {
links := []Link{}
err := json.NewDecoder(rdr).Decode(&links)
return links, err
}
func updateLinksFromRemote(db *LinkDB, path string, cachePath string) error {
log.Printf("Updating golinks from %s\n", path)
r, err := http.Get(path)
if err != nil {
return fmt.Errorf("Could not download updated golinks: %w", err)
}
defer r.Body.Close()
l, err := readLinks(r.Body)
if err != nil {
return fmt.Errorf("Could not parse updated golinks: %w", err)
}
stat := db.Update(l)
log.Printf("Merged %d updated (%d new) golinks\n", len(l), len(stat.Added))
if len(stat.Added) > 0 {
added := []string{}
n := min(5, len(stat.Added))
for _, l := range stat.Added[:n] {
added = append(added, l.Display)
}
log.Printf("Sample (up to 5) of new links: %s\n", added)
err = db.WriteCache(cachePath)
if err != nil {
log.Printf("Could not write to cache: %s", err)
}
}
return nil
}
type LinkDB struct {
once sync.Once
links map[string]Link
}
type LinkStat struct {
Added []Link
}
func (db *LinkDB) Len() int {
return len(db.links)
}
func (db *LinkDB) maybeInit() {
db.once.Do(func() {
db.links = map[string]Link{}
})
}
func (db *LinkDB) Update(links []Link) LinkStat {
db.maybeInit()
stat := LinkStat{[]Link{}}
for _, link := range links {
if _, ok := db.links[link.Source]; !ok {
stat.Added = append(stat.Added, link)
}
db.links[link.Source] = link
}
return stat
}
func (db *LinkDB) LoadJson(path string) error {
f, err := os.Open(path)
if os.IsNotExist(err) {
log.Printf("Loaded 0 golinks from %s: %s\n", path, err.Error())
return nil
}
if err != nil {
return err
}
defer f.Close()
ls, err := readLinks(f)
if err != nil {
return err
}
stat := db.Update(ls)
log.Printf("Loaded %d (%d new) golinks from %s\n", len(ls), len(stat.Added), path)
return nil
}
func (db *LinkDB) WriteCache(path string) error {
lns := make([]Link, 0, len(db.links))
for _, l := range db.links {
lns = append(lns, l)
}
b, err := json.Marshal(lns)
if err != nil {
return err
}
log.Printf("Writing %d golinks to %s\n", len(db.links), path)
return renameio.WriteFile(path, b, 0644)
}
func (db *LinkDB) Lookup(name string) *Link {
if name == "" {
// Special case the empty string - the db has an entry with one :(
return nil
}
l, ok := db.links[canonicalizeLink(name)]
if !ok {
return nil
}
return &l
}