// Copyright 2017 The Mellium Contributors. // Use of this source code is governed by the BSD 2-clause // license that can be found in the LICENSE file. // +build ignore // The gen command creates disco identity options from the disco-categories // registry located at https://xmpp.org/registrar/disco-categories.html. package main import ( "bytes" "encoding/xml" "flag" "go/format" "io" "log" "net/http" "os" "path/filepath" "strings" "text/template" ) type registry struct { XMLName xml.Name `xml:"registry"` Categories []struct { Name string `xml:"name"` Desc string `xml:"desc"` Type []struct { Name string `xml:"name"` XMLLang string `xml:"http://www.w3.org/XML/1998/namespace lang"` Desc string `xml:"desc"` Doc string `xml:"doc"` } `xml:"type"` } `xml:"category"` } func main() { var ( catURL = `https://xmpp.org/registrar/disco-categories.xml` tmpDir = os.TempDir() outPath = `./` ) flag.StringVar(&catURL, "categories", catURL, "A link to the disco-categories registry") flag.StringVar(&tmpDir, "tmp", tmpDir, "A temporary directory to downlaod files to") flag.StringVar(&outPath, "out", outPath, "A directory to output Go files to") flag.Parse() ident := func(s string) string { return strings.Map(func(r rune) rune { switch r { case ' ', '-', '_': return rune(-1) } return r }, s) } tmpl := template.Must(template.New("categories").Funcs(map[string]interface{}{ "ident": ident, "export": func(s string) string { s = strings.Title(s) // Manual tweaks to make the output more idiomatic Go. for _, cap := range []string{ "Rss", "Xmpp", "Smtp", "Sms", "Qq", "Msn", "Lcs", "Irc", "Icq", "Aim", "S2s", "C2s", "Sm", "Pc", "Soap", "Pam", "Ntlm", "Ldap", "Http-Ws", "Mrim", "Ocs", "Pep", "Im", } { if strings.HasPrefix(s, cap) { s = strings.Replace(s, cap, strings.ToUpper(cap), 1) break } } return ident(s) }, }).Parse(`// Code generated by running "go generate" in mellium.im/xmpp/disco. DO NOT EDIT. package disco // Predefined identities generated from the Service Discovery Identities // Registry as registered with the XMPP Registrar. var ( {{- range $cat := .Categories}}{{range .Type}} // Category: {{$cat.Desc}} // Type: {{.Desc}} {{export $cat.Name}}{{export .Name}} = Identity{Category: {{printf "%q" $cat.Name}}, Type: {{printf "%q" .Name}}} {{end}}{{end}} )`)) logger := log.New(os.Stderr, "", log.LstdFlags) if err := genFile(catURL, tmpDir, filepath.Join(outPath, "categories.go"), tmpl); err != nil { logger.Fatal(err) } } func genFile(regURL, tmpDir, outFile string, tmpl *template.Template) error { fd, err := openOrDownload(regURL, tmpDir) if err != nil { return err } defer fd.Close() out, err := os.Create(outFile) if err != nil { return err } defer fd.Close() reg := registry{} d := xml.NewDecoder(fd) if err = d.Decode(®); err != nil { return err } buf := new(bytes.Buffer) if err = tmpl.Execute(buf, reg); err != nil { return err } b, err := format.Source(buf.Bytes()) if err != nil { return err } _, err = io.Copy(out, bytes.NewReader(b)) return err } type writeCloser struct { io.Writer closer func() error } func (f writeCloser) Close() error { return f.closer() } // opens the provided registry URL (downloading it if it doesn't exist). func openOrDownload(catURL, tmpDir string) (*os.File, error) { registryXML := filepath.Join(tmpDir, filepath.Base(catURL)) fd, err := os.Open(registryXML) if err != nil { fd, err = os.Create(registryXML) if err != nil { return nil, err } // If we couldn't open it for reading, attempt to download it. resp, err := http.Get(catURL) if err != nil { return nil, err } _, err = io.Copy(fd, resp.Body) if err != nil { return nil, err } resp.Body.Close() _, err = fd.Seek(0, 0) if err != nil { return nil, err } } return fd, err }