2015-01-29 11:35:15 +01:00
|
|
|
// Copyright 2014 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
// JSONenums is a tool to automate the creation of methods that satisfy the
|
|
|
|
// fmt.Stringer, json.Marshaler and json.Unmarshaler interfaces.
|
|
|
|
// Given the name of a (signed or unsigned) integer type T that has constants
|
2015-06-08 01:16:58 +02:00
|
|
|
// defined, jsonenums will create a new self-contained Go source file implementing
|
2015-01-29 11:35:15 +01:00
|
|
|
//
|
|
|
|
// func (t T) String() string
|
|
|
|
// func (t T) MarshalJSON() ([]byte, error)
|
|
|
|
// func (t *T) UnmarshalJSON([]byte) error
|
|
|
|
//
|
|
|
|
// The file is created in the same package and directory as the package that defines T.
|
|
|
|
// It has helpful defaults designed for use with go generate.
|
|
|
|
//
|
|
|
|
// JSONenums is a simple implementation of a concept and the code might not be
|
|
|
|
// the most performant or beautiful to read.
|
|
|
|
//
|
|
|
|
// For example, given this snippet,
|
|
|
|
//
|
|
|
|
// package painkiller
|
|
|
|
//
|
|
|
|
// type Pill int
|
|
|
|
//
|
|
|
|
// const (
|
|
|
|
// Placebo Pill = iota
|
|
|
|
// Aspirin
|
|
|
|
// Ibuprofen
|
|
|
|
// Paracetamol
|
|
|
|
// Acetaminophen = Paracetamol
|
|
|
|
// )
|
|
|
|
//
|
|
|
|
// running this command
|
|
|
|
//
|
|
|
|
// jsonenums -type=Pill
|
|
|
|
//
|
|
|
|
// in the same directory will create the file pill_jsonenums.go, in package painkiller,
|
|
|
|
// containing a definition of
|
|
|
|
//
|
|
|
|
// func (r Pill) String() string
|
|
|
|
// func (r Pill) MarshalJSON() ([]byte, error)
|
|
|
|
// func (r *Pill) UnmarshalJSON([]byte) error
|
|
|
|
//
|
|
|
|
// That method will translate the value of a Pill constant to the string representation
|
|
|
|
// of the respective constant name, so that the call fmt.Print(painkiller.Aspirin) will
|
|
|
|
// print the string "Aspirin".
|
|
|
|
//
|
|
|
|
// Typically this process would be run using go generate, like this:
|
|
|
|
//
|
2015-06-08 01:16:58 +02:00
|
|
|
// //go:generate jsonenums -type=Pill
|
2015-01-29 11:35:15 +01:00
|
|
|
//
|
|
|
|
// If multiple constants have the same value, the lexically first matching name will
|
|
|
|
// be used (in the example, Acetaminophen will print as "Paracetamol").
|
|
|
|
//
|
|
|
|
// With no arguments, it processes the package in the current directory.
|
|
|
|
// Otherwise, the arguments must name a single directory holding a Go package
|
|
|
|
// or a set of Go source files that represent a single Go package.
|
|
|
|
//
|
|
|
|
// The -type flag accepts a comma-separated list of types so a single run can
|
2015-04-23 18:50:48 +02:00
|
|
|
// generate methods for multiple types. The default output file is
|
|
|
|
// t_jsonenums.go, where t is the lower-cased name of the first type listed.
|
|
|
|
// The suffix can be overridden with the -suffix flag and a prefix may be added
|
|
|
|
// with the -prefix flag.
|
2015-01-29 11:35:15 +01:00
|
|
|
//
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"flag"
|
|
|
|
"go/format"
|
|
|
|
"io/ioutil"
|
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
|
2016-05-04 00:50:41 +02:00
|
|
|
"github.com/campoy/jsonenums/parser"
|
2015-01-29 11:35:15 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
typeNames = flag.String("type", "", "comma-separated list of type names; must be set")
|
2015-04-23 18:50:48 +02:00
|
|
|
outputPrefix = flag.String("prefix", "", "prefix to be added to the output file")
|
2015-01-29 11:35:15 +01:00
|
|
|
outputSuffix = flag.String("suffix", "_jsonenums", "suffix to be added to the output file")
|
2016-11-04 23:03:45 +01:00
|
|
|
|
|
|
|
// BSON Support
|
|
|
|
bsonMode = flag.Bool("bson", false, "enable BSON-mode and generate BSON output in addition to JSON")
|
2015-01-29 11:35:15 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
flag.Parse()
|
|
|
|
if len(*typeNames) == 0 {
|
|
|
|
log.Fatalf("the flag -type must be set")
|
|
|
|
}
|
|
|
|
types := strings.Split(*typeNames, ",")
|
|
|
|
|
|
|
|
// Only one directory at a time can be processed, and the default is ".".
|
2016-05-11 18:02:21 +02:00
|
|
|
dir := "."
|
2015-01-29 11:35:15 +01:00
|
|
|
if args := flag.Args(); len(args) == 1 {
|
2016-05-11 18:02:21 +02:00
|
|
|
dir = args[0]
|
2015-01-29 11:35:15 +01:00
|
|
|
} else if len(args) > 1 {
|
|
|
|
log.Fatalf("only one directory at a time")
|
|
|
|
}
|
2016-05-11 18:02:21 +02:00
|
|
|
dir, err := filepath.Abs(dir)
|
2016-04-21 15:13:48 +02:00
|
|
|
if err != nil {
|
2016-05-11 18:02:21 +02:00
|
|
|
log.Fatalf("unable to determine absolute filepath for requested path %s: %v",
|
|
|
|
dir, err)
|
2016-04-21 15:13:48 +02:00
|
|
|
}
|
2015-01-29 11:35:15 +01:00
|
|
|
|
2016-04-21 15:13:48 +02:00
|
|
|
pkg, err := parser.ParsePackage(dir)
|
2015-01-29 11:35:15 +01:00
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("parsing package: %v", err)
|
|
|
|
}
|
|
|
|
|
2016-11-04 23:03:45 +01:00
|
|
|
funcPrefixes := []string{"JSON"}
|
|
|
|
imports := []string{"encoding/json"}
|
|
|
|
if *bsonMode {
|
|
|
|
funcPrefixes = append(funcPrefixes, "BSON")
|
|
|
|
imports = append(imports, "gopkg.in/mgo.v2/bson")
|
|
|
|
}
|
2015-01-29 11:35:15 +01:00
|
|
|
var analysis = struct {
|
|
|
|
Command string
|
|
|
|
PackageName string
|
|
|
|
TypesAndValues map[string][]string
|
2016-11-04 23:03:45 +01:00
|
|
|
|
|
|
|
// ["JSON", "BSON"]
|
|
|
|
FuncPrefixes []string
|
|
|
|
Imports []string
|
2015-01-29 11:35:15 +01:00
|
|
|
}{
|
|
|
|
Command: strings.Join(os.Args[1:], " "),
|
2015-02-03 07:01:04 +01:00
|
|
|
PackageName: pkg.Name,
|
2015-01-29 11:35:15 +01:00
|
|
|
TypesAndValues: make(map[string][]string),
|
2016-11-04 23:03:45 +01:00
|
|
|
FuncPrefixes: funcPrefixes,
|
|
|
|
Imports: imports,
|
2015-01-29 11:35:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Run generate for each type.
|
|
|
|
for _, typeName := range types {
|
2015-02-03 07:01:04 +01:00
|
|
|
values, err := pkg.ValuesOfType(typeName)
|
2015-01-29 11:35:15 +01:00
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("finding values for type %v: %v", typeName, err)
|
|
|
|
}
|
|
|
|
analysis.TypesAndValues[typeName] = values
|
|
|
|
|
|
|
|
var buf bytes.Buffer
|
|
|
|
if err := generatedTmpl.Execute(&buf, analysis); err != nil {
|
|
|
|
log.Fatalf("generating code: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
src, err := format.Source(buf.Bytes())
|
|
|
|
if err != nil {
|
|
|
|
// Should never happen, but can arise when developing this code.
|
|
|
|
// The user can compile the output to see the error.
|
|
|
|
log.Printf("warning: internal error: invalid Go generated: %s", err)
|
|
|
|
log.Printf("warning: compile the package to analyze the error")
|
|
|
|
src = buf.Bytes()
|
|
|
|
}
|
|
|
|
|
2015-04-23 18:50:48 +02:00
|
|
|
output := strings.ToLower(*outputPrefix + typeName +
|
|
|
|
*outputSuffix + ".go")
|
2015-01-29 11:35:15 +01:00
|
|
|
outputPath := filepath.Join(dir, output)
|
|
|
|
if err := ioutil.WriteFile(outputPath, src, 0644); err != nil {
|
|
|
|
log.Fatalf("writing output: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|