github.com/lheiskan/zebrapack@v4.1.1-0.20181107023619-e955d028f9bf+incompatible/main.go (about) 1 // zebrapack is a code generation tool for 2 // creating methods to serialize and de-serialize 3 // Go data structures to and from ZebraPack (a 4 // schema-based serialization format that is derived 5 // from MessagePack2). 6 // 7 // This package is targeted at the `go generate` tool. 8 // To use it, include the following directive in a 9 // go source file with types requiring source generation: 10 // 11 // //go:generate zebrapack 12 // 13 // The go generate tool should set the proper environment variables for 14 // the generator to execute without any command-line flags. However, the 15 // following options are supported, if you need them (See zebrapack -h): 16 // 17 // $ zebrapack -h 18 // 19 // Usage of zebrapack: 20 // 21 // -msgp 22 // generate msgpack2 serializers instead of ZebraPack; 23 // for backward compatiblity or serializing the zebra 24 // schema itself. 25 // 26 // -fast-strings 27 // for speed when reading a string in a message that won't be 28 // reused, this flag means we'll use unsafe to cast the string 29 // header and avoid allocation. 30 // 31 // -file go generate 32 // input file (or directory); default is $GOFILE, which 33 // is set by the go generate command. 34 // 35 // -genid 36 // generate a fresh random zebraSchemaId64 value to 37 // include in your Go source schema 38 // 39 // -io 40 // create Encode and Decode methods (default true) 41 // 42 // -marshal 43 // create Marshal and Unmarshal methods (default true) 44 // 45 // -method-prefix string 46 // (optional) prefix that will be pre-prended to 47 // the front of generated method names; useful when 48 // you need to avoid namespace collisions, but the 49 // generated tests will break/the msgp package 50 // interfaces won't be satisfied. 51 // 52 // -no-embedded-schema 53 // don't embed the schema in the generated files 54 // 55 // -no-structnames-onwire 56 // don't embed the name of the struct in the 57 // serialized zebrapack. Skipping the embedded 58 // struct names saves time and space and matches 59 // what protocol buffers/thrift/capnproto/msgpack do. 60 // You must know the type on the wire you expect; 61 // or embed a type tag in one universal wrapper 62 // struct. Embedded struct names are a feature 63 // of ZebraPack to help with dynamic language 64 // bindings. 65 // 66 // -o string 67 // output file (default is {input_file}_gen.go 68 // 69 // -schema-to-go string 70 // (standalone functionality) path to schema in msgpack2 71 // format; we will convert it to Go, write the Go on stdout, 72 // and exit immediately 73 // 74 // -tests 75 // create tests and benchmarks (default true) 76 // 77 // -unexported 78 // also process unexported types 79 // 80 // -write-schema string 81 // write schema header to this file; - for stdout 82 // 83 // 84 // For more information, please read README.md, and the wiki at github.com/glycerine/zebrapack 85 // 86 package main 87 88 import ( 89 cryptorand "crypto/rand" 90 "encoding/binary" 91 "flag" 92 "fmt" 93 "io/ioutil" 94 "os" 95 "path/filepath" 96 "strings" 97 98 "github.com/glycerine/zebrapack/cfg" 99 "github.com/glycerine/zebrapack/gen" 100 "github.com/glycerine/zebrapack/parse" 101 "github.com/glycerine/zebrapack/printer" 102 "github.com/glycerine/zebrapack/zebra" 103 ) 104 105 func main() { 106 myflags := flag.NewFlagSet("zebrapack", flag.ExitOnError) 107 c := &cfg.ZebraConfig{} 108 c.DefineFlags(myflags) 109 110 err := myflags.Parse(os.Args[1:]) 111 err = c.ValidateConfig() 112 if err != nil { 113 fmt.Printf("zebrapack command line flag error: '%s'\n", err) 114 os.Exit(1) 115 } 116 117 if c.GenSchemaId { 118 var by [8]byte 119 cryptorand.Read(by[:]) 120 n := binary.LittleEndian.Uint64(by[:]) 121 n &= 0x0001ffffffffffff // restrict to 53 bits so R and js work 122 fmt.Printf("\n// This crypto-randomly generated zebraSchemaId64 is a 53-bit\n"+ 123 "// integer that identifies your namespace.\n"+ 124 "// Paste it into your Go source.\n"+ 125 " const zebraSchemaId64 int64 = 0x%x // %v\n\n", n, n) 126 os.Exit(0) 127 } 128 129 if c.SchemaToGo != "" { 130 handleSchemaToGo(c) 131 os.Exit(0) 132 } 133 134 // GOFILE is set by go generate 135 if c.GoFile == "" { 136 c.GoFile = os.Getenv("GOFILE") 137 if c.GoFile == "" { 138 fmt.Println("No file to parse.") 139 os.Exit(1) 140 } 141 } 142 143 gen.SetFilename(c.GoFile) 144 var mode gen.Method 145 if c.Encode { 146 mode |= (gen.Encode | gen.Decode | gen.Size | gen.FieldsEmpty) 147 } 148 if c.Marshal { 149 mode |= (gen.Marshal | gen.Unmarshal | gen.Size | gen.FieldsEmpty) 150 } 151 if c.Tests { 152 mode |= gen.Test 153 } 154 155 if mode&^gen.Test == 0 { 156 fmt.Println("No methods to generate; -io=false && -marshal=false") 157 os.Exit(1) 158 } 159 160 if err := Run(mode, c); err != nil { 161 fmt.Println(err.Error()) 162 os.Exit(1) 163 } 164 } 165 166 // Run writes all methods using the associated file or path, e.g. 167 // 168 // err := msgp.Run("path/to/myfile.go", gen.Size|gen.Marshal|gen.Unmarshal|gen.Test, false) 169 // 170 func Run(mode gen.Method, c *cfg.ZebraConfig) error { 171 if mode&^gen.Test == 0 { 172 return nil 173 } 174 fmt.Println("======== ZebraPack Code Generator =======") 175 fmt.Printf(">>> Input: \"%s\"\n", c.GoFile) 176 var fs *parse.FileSet 177 var err error 178 if c.NoLoad { 179 fs, err = parse.FileNoLoad(c) 180 } else { 181 fs, err = parse.File(c) 182 } 183 if err != nil { 184 return err 185 } 186 187 if len(fs.Identities) == 0 { 188 fmt.Println("No types requiring code generation were found!") 189 return nil 190 } 191 192 if c.WriteSchema != "" { // saveSchemaAsMsgpackToFile 193 err := fs.SaveMsgpackFile(c.GoFile, c.WriteSchema) 194 if err != nil { 195 panic(err) 196 } 197 } 198 199 return printer.PrintFile(newFilename(c.Out, c.GoFile, fs.Package), fs, mode, c, c.GoFile) 200 } 201 202 // picks a new file name based on input flags and input filename(s). 203 func newFilename(out, old, pkg string) string { 204 if out != "" { 205 if pre := strings.TrimPrefix(out, old); len(pre) > 0 && 206 !strings.HasSuffix(out, ".go") { 207 return filepath.Join(old, out) 208 } 209 return out 210 } 211 212 if fi, err := os.Stat(old); err == nil && fi.IsDir() { 213 old = filepath.Join(old, pkg) 214 } 215 // new file name is old file name + _gen.go 216 return strings.TrimSuffix(old, ".go") + "_gen.go" 217 } 218 219 func fileExists(name string) bool { 220 fi, err := os.Stat(name) 221 if err != nil { 222 return false 223 } 224 if fi.IsDir() { 225 return false 226 } 227 return true 228 } 229 230 func handleSchemaToGo(c *cfg.ZebraConfig) { 231 if !fileExists(c.SchemaToGo) { 232 fmt.Fprintf(os.Stderr, "error: -schema-to-go '%s' path not found\n", c.SchemaToGo) 233 os.Exit(1) 234 } 235 by, err := ioutil.ReadFile(c.SchemaToGo) 236 if err != nil { 237 fmt.Fprintf(os.Stderr, "error: -schema-to-go '%s' produced error on reading file: %v\n", 238 c.SchemaToGo, err) 239 os.Exit(1) 240 } 241 var sch zebra.Schema 242 _, err = sch.UnmarshalMsg(by) 243 if err != nil { 244 fmt.Fprintf(os.Stderr, "error: -schema-to-go '%s' produced error on UnmarshalMsg: %v\n", 245 c.SchemaToGo, err) 246 os.Exit(1) 247 } 248 err = sch.WriteToGo(os.Stdout, c.SchemaToGo, "main") 249 if err != nil { 250 fmt.Fprintf(os.Stderr, "error: -schema-to-go '%s' produced error on UnmarshalMsg: %v\n", 251 c.SchemaToGo, err) 252 os.Exit(1) 253 } 254 }