1// Protocol Buffers for Go with Gadgets
2//
3// Copyright (c) 2013, The GoGo Authors. All rights reserved.
4// http://github.com/gogo/protobuf
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions are
8// met:
9//
10//     * Redistributions of source code must retain the above copyright
11// notice, this list of conditions and the following disclaimer.
12//     * Redistributions in binary form must reproduce the above
13// copyright notice, this list of conditions and the following disclaimer
14// in the documentation and/or other materials provided with the
15// distribution.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29package main
30
31import (
32	"bufio"
33	"bytes"
34	"flag"
35	"fmt"
36	"io/ioutil"
37	"os"
38	"os/exec"
39	"path/filepath"
40	"strings"
41
42	"github.com/gogo/protobuf/version"
43)
44
45type MixMatch struct {
46	Old      []string
47	Filename string
48	Args     []string
49}
50
51func (this MixMatch) Gen(folder string, news []string) {
52	if err := os.MkdirAll(folder, 0777); err != nil {
53		panic(err)
54	}
55	data, err := ioutil.ReadFile(this.Filename)
56	if err != nil {
57		panic(err)
58	}
59	content := string(data)
60	for i, old := range this.Old {
61		if !strings.Contains(content, old) {
62			panic(fmt.Errorf("could not find string {%s} to replace with {%s}", old, news[i]))
63		}
64		content = strings.Replace(content, old, news[i], 1)
65		if strings.Contains(content, old) && old != news[i] {
66			panic(fmt.Errorf("found another string {%s} after it was replaced with {%s}", old, news[i]))
67		}
68	}
69	if err = ioutil.WriteFile(filepath.Join(folder, this.Filename), []byte(content), 0666); err != nil {
70		panic(err)
71	}
72	args := append(this.Args, filepath.Join(folder, this.Filename))
73	var regenerate = exec.Command("protoc", args...)
74	out, err := regenerate.CombinedOutput()
75
76	failed := false
77	scanner := bufio.NewScanner(bytes.NewReader(out))
78	for scanner.Scan() {
79		text := scanner.Text()
80		fmt.Println("protoc-gen-combo: ", text)
81		if !strings.Contains(text, "WARNING") {
82			failed = true
83		}
84	}
85
86	if err != nil {
87		fmt.Print("protoc-gen-combo: error: ", err)
88		failed = true
89	}
90
91	if failed {
92		os.Exit(1)
93	}
94}
95
96func filter(ss []string, flag string) ([]string, string) {
97	s := make([]string, 0, len(ss))
98	var v string
99	for i := range ss {
100		if strings.Contains(ss[i], flag) {
101			vs := strings.Split(ss[i], "=")
102			v = vs[1]
103			continue
104		}
105		s = append(s, ss[i])
106	}
107	return s, v
108}
109
110func filterArgs(ss []string) ([]string, []string) {
111	var args []string
112	var flags []string
113	for i := range ss {
114		if strings.Contains(ss[i], "=") {
115			flags = append(flags, ss[i])
116			continue
117		}
118		args = append(args, ss[i])
119	}
120	return flags, args
121}
122
123func main() {
124	flag.String("version", "2.3.0", "minimum protoc version")
125	flag.Bool("default", true, "generate the case where everything is false")
126	flags, args := filterArgs(os.Args[1:])
127	var min string
128	flags, min = filter(flags, "-version")
129	if len(min) == 0 {
130		min = "2.3.1"
131	}
132	if !version.AtLeast(min) {
133		fmt.Printf("protoc version not high enough to parse this proto file\n")
134		return
135	}
136	if len(args) != 1 {
137		fmt.Printf("protoc-gen-combo expects a filename\n")
138		os.Exit(1)
139	}
140	filename := args[0]
141	var def string
142	flags, def = filter(flags, "-default")
143	if _, err := exec.LookPath("protoc"); err != nil {
144		panic("cannot find protoc in PATH")
145	}
146	m := MixMatch{
147		Old: []string{
148			"option (gogoproto.unmarshaler_all) = false;",
149			"option (gogoproto.marshaler_all) = false;",
150		},
151		Filename: filename,
152		Args:     flags,
153	}
154	if def != "false" {
155		m.Gen("./combos/neither/", []string{
156			"option (gogoproto.unmarshaler_all) = false;",
157			"option (gogoproto.marshaler_all) = false;",
158		})
159	}
160	m.Gen("./combos/marshaler/", []string{
161		"option (gogoproto.unmarshaler_all) = false;",
162		"option (gogoproto.marshaler_all) = true;",
163	})
164	m.Gen("./combos/unmarshaler/", []string{
165		"option (gogoproto.unmarshaler_all) = true;",
166		"option (gogoproto.marshaler_all) = false;",
167	})
168	m.Gen("./combos/both/", []string{
169		"option (gogoproto.unmarshaler_all) = true;",
170		"option (gogoproto.marshaler_all) = true;",
171	})
172}
173