// This file is run by go generate, and generates nerdfont-provider-data.go. // +build generation package main import ( "bufio" "bytes" "io" "log" "net/http" "os" "strconv" "strings" "text/template" "time" "unicode" ) var fileTemplate = template.Must(template.New("").Parse(`// Code generated by go generate; DO NOT EDIT. // This file was generated by robots at // {{ .Timestamp }} // using data from // {{ .URL }} package main var NerdFontGlyphs = []Glyph{ {{- range $key, $rune := .Glyphs }} { Key: {{ printf "%q" $key }}, Rune: 0x{{ printf "%x" $rune }}, }, {{- end }} } `)) const TokenStartString = ".nf-" const ClassStartString = ":before {\n content: \"\\" const ClassEndString = "\";\n}\n" // SplitCSS scans the reader looking for data of the form // .nf-(key):before { // content: "\(hex)"; // } // and returns (key):(hex) when it finds this data. func SplitCSS(data []byte, atEOF bool) (advance int, token []byte, err error) { if !bytes.HasPrefix(data, []byte(TokenStartString)) { // This is not our goal. Skip this line. nextNewline := bytes.IndexRune(data, '\n'); if nextNewline == -1 { // Can't skip this line yet because we don't know where it ends! return 0, nil, nil } else { offset := nextNewline + 1 // Skip this line in our own head and see if we get a result. advance, token, err = SplitCSS(data[offset:], atEOF) if advance > 0 || token != nil || err != nil { // If we do, return it adjusted by the offset. return advance + offset, token, err } else { // Otherwise, just tell the scanner to skip the line we found and keep going. return offset, nil, nil } } } keyEnd := bytes.Index(data, []byte(ClassStartString)) if keyEnd == -1 { // We don't know where the key ends! return 0, nil, nil } key := data[len(TokenStartString):keyEnd] contentEnd := bytes.Index(data, []byte(ClassEndString)) if contentEnd == -1 { // We don't know where the content ends! return 0, nil, nil } content := data[keyEnd + len(ClassStartString):contentEnd] token = make([]byte, len(key) + len(content) + 1) copy(token[:len(key)], key) token[len(key)] = ':' copy(token[len(key) + 1:], content) return contentEnd + len(ClassEndString), token, nil } func SplitOnce(s string, sep string) (string, string) { result := strings.SplitN(s, sep, 2) if len(result) == 2 { return result[0], result[1] } else if len(result) == 1 { return result[0], "" } else { panic("How did we get a different number of strings than 1 or 2 out of SplitN(2)?") } } func main() { const url = "https://raw.githubusercontent.com/ryanoasis/nerd-fonts/master/css/nerd-fonts-generated.css" rsp, err := http.Get(url) if err != nil { log.Fatalf("failed to get css file: %s", err) } defer func(b io.ReadCloser) { err := b.Close() if err != nil { log.Printf("failed closing the css file: %s", err) } }(rsp.Body) sc := bufio.NewScanner(rsp.Body) sc.Split(SplitCSS) glyphs := make(map[string]rune) for sc.Scan() { key, hex := SplitOnce(sc.Text(), ":") val, err := strconv.ParseInt(hex, 16, 64) if err != nil { log.Printf("val %s for %s cannot be parsed as hex: %s", hex, key, err) continue } if val > int64(unicode.MaxRune) { log.Printf("val 0x%x for %s exceeds MaxRune", val, key) continue } if old, ok := glyphs[key]; ok { log.Printf("already stored %s as 0x%x but got 0x%x, replacing", key, old, val) } glyphs[key] = rune(val) } if sc.Err() != nil { log.Fatalf("failed to scan: %s", sc.Err()) } f, err := os.Create("nerdfont-provider-data.go") if err != nil { log.Fatalf("failed to open the data file: %s", err) } defer func(f *os.File) { err := f.Close() if err != nil { log.Printf("failed closing the data file: %s", err) } }(f) err = fileTemplate.Execute(f, struct { Timestamp time.Time URL string Glyphs map[string]rune }{ Timestamp: time.Now(), URL: url, Glyphs: glyphs, }) if err != nil { log.Fatalf("failed writing template to the data file: %s", err) } }