// 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
}