1
2
3
4
5 package main
6
7
8
9
10
11
12
13 import (
14 "flag"
15 "fmt"
16 "io/ioutil"
17 "log"
18 "os"
19 "strings"
20
21 "go.starlark.net/lib/json"
22 starlarkproto "go.starlark.net/lib/proto"
23 "go.starlark.net/resolve"
24 "go.starlark.net/starlark"
25 "google.golang.org/protobuf/encoding/protojson"
26 "google.golang.org/protobuf/encoding/prototext"
27 "google.golang.org/protobuf/proto"
28 "google.golang.org/protobuf/reflect/protodesc"
29 "google.golang.org/protobuf/reflect/protoreflect"
30 "google.golang.org/protobuf/reflect/protoregistry"
31 "google.golang.org/protobuf/types/descriptorpb"
32 )
33
34
35 var (
36 outputFlag = flag.String("output", "text", "output format (text, wire, json)")
37 varFlag = flag.String("var", "result", "the variable to output")
38 descriptors = flag.String("descriptors", "", "comma-separated list of names of files containing proto.FileDescriptorProto messages")
39 )
40
41
42 func init() {
43 flag.BoolVar(&resolve.AllowFloat, "fp", true, "allow floating-point numbers")
44 flag.BoolVar(&resolve.AllowSet, "set", resolve.AllowSet, "allow set data type")
45 flag.BoolVar(&resolve.AllowLambda, "lambda", resolve.AllowLambda, "allow lambda expressions")
46 flag.BoolVar(&resolve.AllowNestedDef, "nesteddef", resolve.AllowNestedDef, "allow nested def statements")
47 }
48
49 func main() {
50 log.SetPrefix("star2proto: ")
51 log.SetFlags(0)
52 flag.Parse()
53 if len(flag.Args()) != 1 {
54 fatalf("requires a single Starlark file name")
55 }
56 filename := flag.Args()[0]
57
58
59
60 pool := protoregistry.GlobalFiles
61
62
63
64 if *descriptors != "" {
65 var fdset descriptorpb.FileDescriptorSet
66 for i, filename := range strings.Split(*descriptors, ",") {
67 data, err := ioutil.ReadFile(filename)
68 if err != nil {
69 log.Fatalf("--descriptors[%d]: %s", i, err)
70 }
71
72 if err := (proto.UnmarshalOptions{Merge: true}).Unmarshal(data, &fdset); err != nil {
73 log.Fatalf("%s does not contain a proto2.FileDescriptorSet: %v", filename, err)
74 }
75 }
76
77 files, err := protodesc.NewFiles(&fdset)
78 if err != nil {
79 log.Fatalf("protodesc.NewFiles: could not build FileDescriptor index: %v", err)
80 }
81 pool = files
82 }
83
84
85 thread := &starlark.Thread{
86 Print: func(_ *starlark.Thread, msg string) { fmt.Println(msg) },
87 }
88 starlarkproto.SetPool(thread, pool)
89 predeclared := starlark.StringDict{
90 "proto": starlarkproto.Module,
91 "json": json.Module,
92 }
93 globals, err := starlark.ExecFile(thread, filename, nil, predeclared)
94 if err != nil {
95 if evalErr, ok := err.(*starlark.EvalError); ok {
96 fatalf("%s", evalErr.Backtrace())
97 } else {
98 fatalf("%s", err)
99 }
100 }
101
102
103
104
105 result, ok := globals[*varFlag]
106 if !ok {
107 fatalf("%s must define a module-level variable named %q", filename, *varFlag)
108 }
109 msgwrap, ok := result.(*starlarkproto.Message)
110 if !ok {
111 fatalf("got %s, want proto.Message, for %q", result.Type(), *varFlag)
112 }
113 msg := msgwrap.Message()
114
115
116 var marshal func(protoreflect.ProtoMessage) ([]byte, error)
117 switch *outputFlag {
118 case "wire":
119 marshal = proto.Marshal
120
121 case "text":
122 marshal = prototext.MarshalOptions{Multiline: true, Indent: "\t"}.Marshal
123
124 case "json":
125 marshal = protojson.MarshalOptions{Multiline: true, Indent: "\t"}.Marshal
126
127 default:
128 fatalf("unsupported -output format: %s", *outputFlag)
129 }
130 data, err := marshal(msg)
131 if err != nil {
132 fatalf("%s", err)
133 }
134 os.Stdout.Write(data)
135 }
136
137 func fatalf(format string, args ...interface{}) {
138 fmt.Fprintf(os.Stderr, "star2proto: ")
139 fmt.Fprintf(os.Stderr, format, args...)
140 fmt.Fprintln(os.Stderr)
141 os.Exit(1)
142 }
143
View as plain text