internal/generator/main.go (view raw)
1package main
2
3import (
4 "bufio"
5 "bytes"
6 "fmt"
7 "go/format"
8 "io"
9 "net/http"
10 "os"
11 "sort"
12 "text/template"
13 "time"
14)
15
16const (
17 constantsFile = "constants.go"
18 aliasesFile = "map.go"
19)
20
21// customEmojis is the list of emojis which unicode and gemoji databases don't have.
22var customEmojis = map[string]string{
23 ":robot_face:": "\U0001f916", // slack
24}
25
26func main() {
27 emojis, err := fetchEmojis()
28 if err != nil {
29 panic(err)
30 }
31
32 gemojis, err := fetchGemojis()
33 if err != nil {
34 panic(err)
35 }
36
37 constants := generateConstants(emojis)
38 aliases := generateAliases(emojis, gemojis)
39
40 if err = save(constantsFile, emojiListURL, constants); err != nil {
41 panic(err)
42 }
43
44 if err = save(aliasesFile, gemojiURL, aliases); err != nil {
45 panic(err)
46 }
47}
48
49func generateConstants(emojis *groups) string {
50 var res string
51 for _, grp := range emojis.Groups {
52 res += fmt.Sprintf("\n// GROUP: %v\n", grp.Name)
53 for _, subgrp := range grp.Subgroups {
54 res += fmt.Sprintf("// SUBGROUP: %v\n", subgrp.Name)
55 for _, c := range subgrp.Constants {
56 res += emojiConstant(subgrp.Emojis[c])
57 }
58 }
59 }
60
61 return res
62}
63
64func emojiConstant(emojis []emoji) string {
65 basic := emojis[0]
66 switch len(emojis) {
67 case 1:
68 return fmt.Sprintf("%s Emoji = %+q // %s\n", basic.Constant, basic.Code, basic.Name)
69 case 6:
70 oneTonedCode := replaceTones(emojis[1].Code)
71 defaultTone := defaultTone(basic.Code, oneTonedCode)
72
73 if defaultTone != "" {
74 return fmt.Sprintf("%s EmojiWithTone = newEmojiWithTone(%+q).withDefaultTone(%+q) // %s\n",
75 basic.Constant, oneTonedCode, defaultTone, basic.Name)
76 }
77
78 return fmt.Sprintf("%s EmojiWithTone = newEmojiWithTone(%+q) // %s\n",
79 basic.Constant, oneTonedCode, basic.Name)
80 case 26, 20:
81 oneTonedCode := replaceTones(emojis[1].Code)
82 twoTonedCode := replaceTones(emojis[2].Code)
83
84 return fmt.Sprintf("%s EmojiWithTone = newEmojiWithTone(%+q, %+q) // %s\n",
85 basic.Constant, oneTonedCode, twoTonedCode, basic.Name)
86 default:
87 panic(fmt.Errorf("not expected emoji count for a constant: %v", len(emojis)))
88 }
89}
90
91func generateAliases(emojis *groups, gemojis map[string]string) string {
92 var aliases []string
93 var emojiMap = make(map[string]string)
94
95 for _, grp := range emojis.Groups {
96 for _, subgrp := range grp.Subgroups {
97 for _, c := range subgrp.Constants {
98 emoji := subgrp.Emojis[c][0]
99 alias := makeAlias(snakeCase(emoji.Constant))
100 aliases = append(aliases, alias)
101 emojiMap[alias] = emoji.Code
102 }
103 }
104 }
105
106 // add gemoji aliases
107 {
108 for alias, code := range gemojis {
109 _, ok := emojiMap[alias]
110 if !ok {
111 aliases = append(aliases, alias)
112 }
113 emojiMap[alias] = code
114 }
115 }
116
117 // add custom emoji aliases
118 {
119 for alias, code := range customEmojis {
120 _, ok := emojiMap[alias]
121 if !ok {
122 aliases = append(aliases, alias)
123 }
124 emojiMap[alias] = code
125 }
126 }
127
128 var r string
129 sort.Strings(aliases)
130 for _, alias := range aliases {
131 r += fmt.Sprintf("%q: %+q,\n", alias, emojiMap[alias])
132 }
133
134 return r
135}
136func save(filename, url, data string) error {
137 tmpl, err := template.ParseFiles(fmt.Sprintf("internal/generator/%v.tmpl", filename))
138 if err != nil {
139 return err
140 }
141
142 d := struct {
143 Link string
144 Date string
145 Data string
146 }{
147 Link: url,
148 Date: time.Now().Format(time.RFC3339),
149 Data: data,
150 }
151
152 var w bytes.Buffer
153 if err = tmpl.Execute(&w, d); err != nil {
154 return err
155 }
156
157 content, err := format.Source(w.Bytes())
158 if err != nil {
159 return fmt.Errorf("could not format file: %v", err)
160 }
161
162 file, err := os.Create(filename)
163 if err != nil {
164 return fmt.Errorf("could not create file: %v", err)
165 }
166 defer file.Close()
167
168 if _, err := file.Write(content); err != nil {
169 return fmt.Errorf("could not write to file: %v", err)
170 }
171 return nil
172}
173
174func fetchData(url string) ([]byte, error) {
175 resp, err := http.Get(url)
176 if err != nil {
177 return nil, err
178 }
179 defer resp.Body.Close()
180
181 // Check server response
182 if resp.StatusCode != http.StatusOK {
183 return nil, fmt.Errorf("bad status: %s", resp.Status)
184 }
185
186 b, err := io.ReadAll(resp.Body)
187 if err != nil {
188 return nil, err
189 }
190
191 return b, nil
192}
193
194func readLines(b []byte, fn func(string)) error {
195 reader := bufio.NewReader(bytes.NewReader(b))
196
197 var line string
198 var err error
199 for {
200 line, err = reader.ReadString('\n')
201 if err != nil {
202 break
203 }
204
205 fn(line)
206 }
207
208 if err != io.EOF {
209 return err
210 }
211
212 return nil
213}