irc-medialink/util/apigen/main.go

156 lines
3.3 KiB
Go

package main
import (
"bytes"
"fmt"
"go/format"
"io/ioutil"
"os"
"path/filepath"
"sort"
"gopkg.in/alecthomas/kingpin.v2"
"gopkg.in/yaml.v2"
"github.com/serenize/snaker"
)
var (
flagPackageName = kingpin.Flag("pkg", "Output package name").Default("main").String()
flagForce = kingpin.Flag("force", "Force rebuild.").Short('f').Bool()
flagYamlDefFile = kingpin.Arg("path", "Input YAML definitions file path").ExistingFile()
)
type yamlDef struct {
Imports []string
Kinds map[string]*yamlTypeDef
}
type yamlTypeDef struct {
Extends []string
Fields map[string]string
}
func normalizeFieldId(t string) string {
switch t {
case "urn":
return "URN"
default:
return snaker.SnakeToCamel(t)
}
}
func main() {
kingpin.Parse()
// Generate output file name from input file name
outputFileName := (*flagYamlDefFile)[0:len(*flagYamlDefFile)-len(filepath.Ext(*flagYamlDefFile))] + ".go"
// Get input file stat
inputStat, err := os.Stat(*flagYamlDefFile)
if err != nil {
panic(err)
}
// Does output file exist?
if !*flagForce {
if s, err := os.Stat(outputFileName); err == nil {
// Check output file time against input file time
if !inputStat.ModTime().After(s.ModTime()) {
// Output already up to date!
return
}
}
}
fmt.Println(filepath.Base(*flagYamlDefFile))
// Read YAML def
ymlBytes, err := ioutil.ReadFile(*flagYamlDefFile)
if err != nil {
panic(err)
}
// Unmarshal YAML def
var ymlDef yamlDef
yaml.Unmarshal(ymlBytes, &ymlDef)
b := new(bytes.Buffer)
// Write package name
b.WriteString(fmt.Sprintf("package %s\n", *flagPackageName))
// Write imports
if ymlDef.Imports != nil && len(ymlDef.Imports) > 0 {
b.WriteString("import (\n")
for _, i := range ymlDef.Imports {
b.WriteString(fmt.Sprintf("%q", i))
}
b.WriteString(")\n")
}
// Write types
registeredTypes := make(map[string]interface{})
//var ensureType func(string)
generateType := func(name string, definition *yamlTypeDef) {
//id := snaker.SnakeToCamel(name)
/*for _, fieldType := range definition.Fields {
ensureType(fieldType)
}*/
b.WriteString(fmt.Sprintf("\ntype %s struct {\n", name))
// Extends (as configured)
for _, t := range definition.Extends {
b.WriteString(t + "\n")
}
// Fields (sorted alphabetically)
var keys []string
for k, _ := range definition.Fields {
keys = append(keys, k)
}
sort.Strings(keys)
for _, fieldName := range keys {
fieldType := definition.Fields[fieldName]
fieldId := normalizeFieldId(fieldName)
b.WriteString(fmt.Sprintf("%s %s `json:%q`\n", fieldId, fieldType, fieldName))
}
b.WriteString("}\n")
}
/*ensureType = func(name string) {
if _, ok := registeredTypes[name]; ok {
// already generated
return
}
typeDef, ok := ymlDef.Kinds[name]
if ok {
registeredTypes[name] = nil
generateType(name, typeDef)
}
}*/
var keys []string
for k, _ := range ymlDef.Kinds {
keys = append(keys, k)
}
sort.Strings(keys)
for _, name := range keys {
typeDef := ymlDef.Kinds[name]
if _, ok := registeredTypes[name]; !ok {
registeredTypes[name] = nil
generateType(name, typeDef)
}
}
// Write out formatted source code
//fmt.Println(string(b.Bytes()))
fb, err := format.Source(b.Bytes())
if err != nil {
panic(err)
}
ioutil.WriteFile(outputFileName, fb, 0644)
}