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