-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
147 lines (123 loc) · 4.18 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package main
import (
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
)
var (
typeNames = flag.String("type", "", "comma-separated list of type names; must be set")
templateFile = flag.String("template", "", "template file path which is relative from the srcdir; must be set")
templateData = flag.String("data", "", "semicolon-separated list of extra template data. each data coupled by equal sign; e.g. \"typename=Foo;prefix=Bar\"")
outputFile = flag.String("output", "", "output file name; default srcdir/<snake-cased-type>_constconv.go")
buildTags = flag.String("tags", "", "comma-separated list of build tags to apply")
)
// Usage is a replacement usage function for the flag package.
func Usage() {
_, _ = fmt.Fprintf(os.Stderr, `Usage of constconv:
constconv -type T -template F [optional flags] [directory]
constconv -type T -template F [optional flags] files... # Must be a single package
`) // TODO: print go.dev package URL
flag.PrintDefaults()
}
func main() {
log.SetPrefix("constconv: ")
flag.Usage = Usage
flag.Parse()
config := preprocess()
gen := NewGenerator(config)
if err := gen.LoadTemplate(); err != nil {
log.Fatalf("loading template output: %s", err)
}
parser := NewParser(config)
if err := parser.Parse(); err != nil {
log.Fatalf("parsing output: %s", err)
}
src, err := gen.Generate(parser)
if err != nil {
log.Fatalf("generating output: %s", err)
}
if err := ioutil.WriteFile(config.outputFile, src, 0644); err != nil {
log.Fatalf("writing output: %s", err)
}
}
type Config struct {
types []string // The list of type names. e.g.: ["UserStatus", "os.FileMode"]
tags []string // The list of build tags.
extraData map[string]string // The map of extra template data
execArgsStr string // The argument string executed
dirOrFiles []string // The arguments
baseDir string // The base directory
templateFile string // The template file name.
outputFile string // The output file name.
}
// preprocess processes flags and initializes its related. It fatal-exit if error occurred.
func preprocess() *Config {
config := &Config{}
if len(*typeNames) == 0 || len(*templateFile) == 0 {
flag.Usage()
os.Exit(2)
}
config.types = strings.Split(*typeNames, ",")
if len(*buildTags) > 0 {
config.tags = strings.Split(*buildTags, ",")
}
// convert "typename=Foo;prefix=Bar" into {"typename":"Foo","prefix":"Bar"}
config.extraData = make(map[string]string)
if len(*templateData) > 0 {
kvPairs := strings.Split(*templateData, ";")
for _, kvPair := range kvPairs {
kv := strings.SplitN(kvPair, "=", 2)
if len(kv) != 2 {
log.Fatalf("invalid extraData: %s", kvPair)
}
config.extraData[kv[0]] = kv[1]
}
}
// accept either one directory or a list of files.
config.dirOrFiles = flag.Args()
if len(config.dirOrFiles) == 0 {
config.dirOrFiles = []string{"."}
}
dir, dirSpecified, err := detectDirectory(config.dirOrFiles)
if err != nil {
log.Fatal(err)
} else if dirSpecified && len(config.tags) != 0 {
log.Fatal("-tags option applies only to directories, not when files are specified")
}
config.baseDir = dir
config.outputFile = *outputFile
if config.outputFile == "" {
underscoreTypeName := strings.Replace(config.types[0], ".", "_", 1)
baseName := fmt.Sprintf("%s_constconv.go", underscoreTypeName)
config.outputFile = filepath.Join(config.baseDir, strings.ToLower(baseName))
}
config.templateFile = filepath.Join(config.baseDir, *templateFile)
config.execArgsStr = strings.Join(os.Args[1:], " ")
return config
}
// detectDirectory detects the base directory by arguments.
func detectDirectory(dirOrFiles []string) (dir string, dirSpecified bool, err error) {
if len(dirOrFiles) == 1 {
if isDir, err := isDirectory(dirOrFiles[0]); err != nil {
return "", false, err
} else if isDir {
dir = dirOrFiles[0]
return dir, true, nil
}
}
dir = filepath.Dir(dirOrFiles[0])
return dir, false, nil
}
// Helper
// isDirectory reports whether the named file is a directory.
func isDirectory(name string) (bool, error) {
info, err := os.Stat(name)
if err != nil {
return false, err
}
return info.IsDir(), nil
}