Skip to content

Commit aaab42a

Browse files
authored
Merge pull request #4 from coreos/fix
parse: support grpc-gateway
2 parents 150f6f9 + 84aba29 commit aaab42a

File tree

9 files changed

+1059
-699
lines changed

9 files changed

+1059
-699
lines changed

main.go

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,18 @@
1515
// protodoc generates Protocol Buffer documentation.
1616
//
1717
// Usage:
18-
// protodoc [flags]
18+
// protodoc [flags]
1919
//
2020
// Flags:
21-
// --directories value comma separated map of target directory to parse options (e.g. 'dirA=message,dirB=message_service')
22-
// -d, --directory string target directory where Protocol Buffer files are.
23-
// -h, --help help for protodoc
24-
// -l, --languages value language options in field descriptions (Go, C++, Java, Python, Ruby, C#) (default [])
25-
// --message-only-from-this-file string if specified, it parses only the messages in this file within the directory
26-
// -o, --output string output file path to save documentation
27-
// -p, --parse value Protocol Buffer types to parse (message, service) (default [service,message])
28-
// -t, --title string title of documentation
21+
// --directories=: comma separated map of target directory to parse options (e.g. 'dirA=message,dirB=message_service')
22+
// -d, --directory="": target directory where Protocol Buffer files are.
23+
// -c, --disclaimer="": disclaimer statement
24+
// -h, --help[=false]: help for protodoc
25+
// -l, --languages=[]: language options in field descriptions (Go, C++, Java, Python, Ruby, C#)
26+
// --message-only-from-this-file="": if specified, it parses only the messages in this file within the directory
27+
// -o, --output="": output file path to save documentation
28+
// -p, --parse=[service,message]: Protocol Buffer types to parse (message, service)
29+
// -t, --title="": title of documentation
2930
//
3031
package main
3132

@@ -52,6 +53,7 @@ var (
5253
languageOptions []string
5354
title string
5455
outputPath string
56+
disclaimer string
5557

5658
targetDirectories = newDirectoryOptions()
5759
messageOnlyFromThisFile string
@@ -106,6 +108,7 @@ func init() {
106108
rootCommand.PersistentFlags().StringSliceVarP(&languageOptions, "languages", "l", []string{}, "language options in field descriptions (Go, C++, Java, Python, Ruby, C#)")
107109
rootCommand.PersistentFlags().StringVarP(&title, "title", "t", "", "title of documentation")
108110
rootCommand.PersistentFlags().StringVarP(&outputPath, "output", "o", "", "output file path to save documentation")
111+
rootCommand.PersistentFlags().StringVarP(&disclaimer, "disclaimer", "c", "", "disclaimer statement")
109112

110113
rootCommand.PersistentFlags().Var(&targetDirectories, "directories", "comma separated map of target directory to parse options (e.g. 'dirA=message,dirB=message_service')")
111114
rootCommand.PersistentFlags().StringVar(&messageOnlyFromThisFile, "message-only-from-this-file", "", "if specified, it parses only the messages in this file within the directory")
@@ -129,7 +132,7 @@ func CommandFunc(cmd *cobra.Command, args []string) error {
129132
}
130133
}
131134
log.Println("converting to markdown", title)
132-
rs, err = proto.Markdown(title, opts, languageOptions...)
135+
rs, err = proto.Markdown(disclaimer, title, opts, languageOptions...)
133136
if err != nil {
134137
return err
135138
}
@@ -147,7 +150,7 @@ func CommandFunc(cmd *cobra.Command, args []string) error {
147150
if err != nil {
148151
return err
149152
}
150-
ms, err := proto.Markdown("", elem.options, languageOptions...)
153+
ms, err := proto.Markdown(disclaimer, "", elem.options, languageOptions...)
151154
if err != nil {
152155
return err
153156
}

parse/markdown.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,13 @@ const (
2929

3030
// Markdown saves 'Proto' to markdown documentation.
3131
// lopts are a slice of language options (C++, Java, Python, Go, Ruby, C#).
32-
func (p *Proto) Markdown(title string, parseOpts []ParseOption, lopts ...string) (string, error) {
32+
func (p *Proto) Markdown(disclaimer, title string, parseOpts []ParseOption, lopts ...string) (string, error) {
3333
p.Sort()
3434

3535
buf := new(bytes.Buffer)
36+
if len(disclaimer) > 0 {
37+
buf.WriteString(fmt.Sprintf("%s\n\n\n", disclaimer))
38+
}
3639
if len(title) > 0 {
3740
buf.WriteString(fmt.Sprintf("### %s\n\n\n", title))
3841
}

parse/markdown_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func TestMarkdown(t *testing.T) {
2121
if err != nil {
2222
t.Fatal(err)
2323
}
24-
if txt, err := proto.Markdown("etcdserverpb", []ParseOption{ParseService, ParseMessage}, "Go", "Java", "Python", "C++"); err != nil {
24+
if txt, err := proto.Markdown("this is test...", "etcdserverpb", []ParseOption{ParseService, ParseMessage}, "Go", "Java", "Python", "C++"); err != nil {
2525
t.Fatal(err)
2626
} else {
2727
err = toFile(txt, "testdata/README.md")

parse/parse.go

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@ func readLines(r io.Reader) ([]string, error) {
2727
scanner = bufio.NewScanner(r)
2828
)
2929
for scanner.Scan() {
30-
// remove indents
30+
// remove indentation
3131
line := strings.TrimSpace(scanner.Text())
32+
33+
// skip empty line
3234
if len(line) > 0 {
3335
if strings.HasPrefix(line, "//") {
3436
lines = append(lines, line)
@@ -38,12 +40,15 @@ func readLines(r io.Reader) ([]string, error) {
3840
// remove semi-colon line-separator
3941
sl := strings.Split(line, ";")
4042
for _, txt := range sl {
41-
if len(txt) > 0 {
42-
if strings.HasPrefix(txt, "optional ") { // proto2
43-
txt = strings.Replace(txt, "optional ", "", 1)
44-
}
45-
lines = append(lines, txt)
43+
txt = strings.TrimSpace(txt)
44+
if len(txt) == 0 {
45+
continue
4646
}
47+
48+
if strings.HasPrefix(txt, "optional ") { // proto2
49+
txt = strings.Replace(txt, "optional ", "", 1)
50+
}
51+
lines = append(lines, txt)
4752
}
4853
}
4954
}
@@ -122,23 +127,49 @@ func ReadFile(fpath string) (*Proto, error) {
122127
)
123128

124129
skippingEnum := false
130+
skippingGatewayCnt := 0
125131
for _, line := range lines {
126132
if strings.HasPrefix(line, "//") {
127133
ls := strings.Replace(line, "//", "", 1)
128134
comments = append(comments, strings.TrimSpace(ls))
129135
continue
130136
}
137+
138+
// skippin enum
131139
if strings.HasPrefix(line, "enum ") {
132140
skippingEnum = true
133141
continue
134142
}
143+
if skippingEnum && strings.HasSuffix(line, "}") { // end of enum
144+
skippingEnum = false
145+
continue
146+
}
135147
if skippingEnum {
136-
if strings.HasSuffix(line, "}") { // end of enum
137-
skippingEnum = false
138-
}
139148
continue
140149
}
141150

151+
// skip gengo/grpc-gateway
152+
if strings.HasPrefix(line, "option (google.api.http) = ") {
153+
skippingGatewayCnt++
154+
continue
155+
}
156+
if strings.HasPrefix(line, "post: ") {
157+
skippingGatewayCnt++
158+
continue
159+
}
160+
if strings.HasPrefix(line, "body: ") {
161+
skippingGatewayCnt++
162+
continue
163+
}
164+
if skippingGatewayCnt == 3 && line == "}" {
165+
skippingGatewayCnt++
166+
continue
167+
}
168+
if skippingGatewayCnt == 4 {
169+
skippingGatewayCnt = 0
170+
continue // end of grpc-gateway
171+
}
172+
142173
switch mode {
143174
case reading:
144175
for j, elem := range strings.Fields(line) {
@@ -205,7 +236,8 @@ func ReadFile(fpath string) (*Proto, error) {
205236
if strings.HasPrefix(line, "rpc ") {
206237
lt := strings.Replace(line, "rpc ", "", 1)
207238
lt = strings.Replace(lt, ")", "", -1)
208-
lt = strings.Replace(lt, " {}", "", 1)
239+
lt = strings.Replace(lt, " {}", "", 1) // without grpc gateway
240+
lt = strings.Replace(lt, " {", "", 1) // with grpc gateway
209241
fsigs := strings.Split(lt, " returns ")
210242

211243
ft := strings.Split(fsigs[0], "(") // split 'Watch(stream WatchRequest'
@@ -224,7 +256,8 @@ func ReadFile(fpath string) (*Proto, error) {
224256
protoMethod.Description = strings.Join(comments, " ")
225257
protoService.Methods = append(protoService.Methods, protoMethod)
226258
comments = []string{}
227-
} else if !strings.HasSuffix(line, "{}") && strings.HasSuffix(line, "}") { // closing of service
259+
} else if !strings.HasSuffix(line, "{}") && strings.HasSuffix(line, "}") {
260+
// end of service
228261
protoService.FilePath = fs
229262
rp.Services = append(rp.Services, protoService)
230263
protoService = ProtoService{}

parse/parse_test.go

Lines changed: 0 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -22,59 +22,4 @@ func TestReadDir(t *testing.T) {
2222
t.Fatal(err)
2323
}
2424
proto.Sort()
25-
26-
if proto.Messages[51].Name != "MemberListRequest" {
27-
t.Fatalf("expected 'MemberListRequest', got %+v", proto.Messages[51])
28-
}
29-
if proto.Messages[52].Name != "MemberListResponse" {
30-
t.Fatalf("expected 'MemberListResponse', got %+v", proto.Messages[52])
31-
}
32-
if proto.Messages[52].Fields[0].Name != "header" {
33-
t.Fatalf("expected 'header', got %+v", proto.Messages[52])
34-
}
35-
if proto.Messages[52].Fields[0].UserDefinedProtoType != "ResponseHeader" {
36-
t.Fatalf("expected 'ResponseHeader', got %+v", proto.Messages[52])
37-
}
38-
39-
if proto.Messages[75].Name != "WatchResponse" {
40-
t.Fatalf("expected 'WatchResponse', got %+v", proto.Messages[75])
41-
}
42-
if proto.Messages[75].Fields[0].Name != "header" {
43-
t.Fatalf("expected 'header', got %+v", proto.Messages[75])
44-
}
45-
if proto.Messages[75].Fields[1].ProtoType != Int64 {
46-
t.Fatalf("expected 'Int64', got %+v", proto.Messages[75])
47-
}
48-
if proto.Messages[75].Fields[1].Name != "watch_id" {
49-
t.Fatalf("expected 'watch_id', got %+v", proto.Messages[75])
50-
}
51-
if proto.Messages[75].Fields[0].UserDefinedProtoType != "ResponseHeader" {
52-
t.Fatalf("expected 'ResponseHeader', got %+v", proto.Messages[75])
53-
}
54-
55-
if len(proto.Services[3].Methods) != 3 {
56-
t.Fatalf("expected 3 methods, got %+v", proto.Services[3].Methods)
57-
}
58-
if proto.Services[3].Methods[0].Name != "LeaseGrant" {
59-
t.Fatalf("expected 'LeaseGrant', got %+v", proto.Services[3].Methods[0])
60-
}
61-
if proto.Services[3].Methods[1].Name != "LeaseRevoke" {
62-
t.Fatalf("expected 'LeaseRevoke', got %+v", proto.Services[3].Methods[1])
63-
}
64-
if proto.Services[3].Methods[2].Name != "LeaseKeepAlive" {
65-
t.Fatalf("expected 'LeaseKeepAlive', got %+v", proto.Services[3].Methods[2])
66-
}
67-
68-
if proto.Services[5].Name != "Watch" {
69-
t.Fatalf("expected 'Watch', got %+v", proto.Services[5])
70-
}
71-
if proto.Services[5].Methods[0].Name != "Watch" {
72-
t.Fatalf("expected 'Watch', got %+v", proto.Services[5])
73-
}
74-
if proto.Services[5].Methods[0].RequestType != "WatchRequest" {
75-
t.Fatalf("expected 'WatchRequest', got %+v", proto.Services[5])
76-
}
77-
if proto.Services[5].Methods[0].ResponseType != "WatchResponse" {
78-
t.Fatalf("expected 'WatchRequest', got %+v", proto.Services[5])
79-
}
8025
}

0 commit comments

Comments
 (0)