fuzzyworld

find-stone-beer / files / beers-info.go

beers-info.go

4.5 KB · history · blame

1package main
2
3import (
4 "bytes"
5 "encoding/json"
6 "errors"
7 "strings"
8
9 "golang.org/x/net/html"
10)
11
12type Core struct {
13 Beers BeersInfo
14}
15
16func (c *Core) UnmarshalJSON(data []byte) error {
17 var rawMap map[string]json.RawMessage
18 err := json.Unmarshal(data, &rawMap)
19 if err != nil {
20 return err
21 }
22 for key, value := range rawMap {
23 if key == "findstone" {
24 err = json.Unmarshal(value, &c.Beers)
25 if err != nil {
26 return err
27 }
28 }
29 }
30 return nil
31}
32
33type BeersInfo struct {
34 brands map[string]Brand
35 PackageTypes map[string]PackageType
36}
37
38func (f *BeersInfo) UnmarshalJSON(data []byte) error {
39 var rawMap map[string]json.RawMessage
40 err := json.Unmarshal(data, &rawMap)
41 if err != nil {
42 return err
43 }
44 for key, value := range rawMap {
45 if key == "brands" {
46 err = json.Unmarshal(value, &f.brands)
47 if err != nil {
48 return err
49 }
50 } else if key == "package_types" {
51 err = json.Unmarshal(value, &f.PackageTypes)
52 if err != nil {
53 return err
54 }
55 }
56 }
57 return nil
58}
59func (f *BeersInfo) FindBrandById(targetId string) (bool, Brand) {
60 for id, brand := range f.brands {
61 if id == targetId {
62 return true, brand
63 }
64 foundBrand, brand := brand.FindBrandById(targetId)
65 if foundBrand {
66 return true, brand
67 }
68 }
69 return false, Brand{}
70}
71
72func GetBeersInfo() (BeersInfo, error) {
73 data, err := getCachedUrlFunc(
74 "https://find.stonebrewing.com",
75 "find-stone-beer-core",
76 "2006-01-02",
77 func(d []byte) ([]byte, error) {
78 reader := bytes.NewReader(d)
79 doc, err := html.Parse(reader)
80 if err != nil {
81 return nil, err
82 }
83
84 var findNodeWithText func(node *html.Node, text string) (bool, *html.Node)
85 findNodeWithText = func(node *html.Node, text string) (bool, *html.Node) {
86 if node.Type == html.TextNode && strings.Contains(node.Data, text) {
87 return true, node
88 }
89
90 if node.FirstChild != nil {
91 found, child := findNodeWithText(node.FirstChild, text)
92 if found {
93 return true, child
94 }
95 }
96
97 if node.NextSibling != nil {
98 found, sibling := findNodeWithText(node.NextSibling, text)
99 if found {
100 return true, sibling
101 }
102 }
103
104 return false, nil
105 }
106 found, node := findNodeWithText(doc, "6376")
107 if !found {
108 return nil, errors.New("could not find node")
109 }
110
111 firstCurly := strings.Index(node.Data, "{")
112 lastCurly := strings.LastIndex(node.Data, "}")
113 jsonText := node.Data[firstCurly : lastCurly+1]
114 return []byte(jsonText), nil
115 })
116 if err != nil {
117 return BeersInfo{}, err
118 }
119
120 var core = Core{}
121 err = json.Unmarshal(data, &core)
122 if err != nil {
123 return BeersInfo{}, err
124 }
125 return core.Beers, nil
126}
127
128type PackageType struct {
129 Name string `json:"name"`
130 ReleaseDate int `json:"release_date"`
131}
132
133type Brand struct {
134 Parent *Brand
135 Name string `json:"name"`
136 ReleaseDate int `json:"release_date"`
137 BrandColor string `json:"brand_color"`
138 BeerStyle string `json:"beer_style"`
139 Logo string `json:"logo"`
140 Abv string `json:"abv"`
141 Children map[string]Brand `json:"children"`
142}
143
144func (b *Brand) UnmarshalJSON(data []byte) error {
145 brand := &struct {
146 Name string `json:"name"`
147 ReleaseDate int `json:"release_date"`
148 BrandColor string `json:"brand_color"`
149 BeerStyle string `json:"beer_style"`
150 Logo string `json:"logo"`
151 Abv string `json:"abv"`
152 Children map[string]json.RawMessage `json:"children"`
153 }{}
154 json.Unmarshal(data, &brand)
155
156 newBrand := Brand{
157 Name: brand.Name,
158 ReleaseDate: brand.ReleaseDate,
159 BrandColor: brand.BrandColor,
160 BeerStyle: brand.BeerStyle,
161 Logo: brand.Logo,
162 Abv: brand.Abv,
163 }
164
165 newBrand.Children = map[string]Brand{}
166 for key, value := range brand.Children {
167 var childName string
168 err := json.Unmarshal(value, &childName)
169 if err != nil {
170 return err
171 }
172 childBrand := Brand{
173 Name: childName,
174 ReleaseDate: brand.ReleaseDate,
175 BrandColor: brand.BrandColor,
176 BeerStyle: brand.BeerStyle,
177 Logo: brand.Logo,
178 Abv: brand.Abv,
179 Parent: &newBrand,
180 }
181 newBrand.Children[key] = childBrand
182 }
183
184 *b = newBrand
185 return nil
186}
187
188func (b *Brand) FindBrandById(targetId string) (bool, Brand) {
189 for id, child := range b.Children {
190 if id == targetId {
191 return true, child
192 }
193 foundBrand, brand := child.FindBrandById(targetId)
194 if foundBrand {
195 return true, brand
196 }
197 }
198 return false, Brand{}
199}