1// Copyright (c) 2017 Ernest Micklei 2// 3// MIT License 4// 5// Permission is hereby granted, free of charge, to any person obtaining 6// a copy of this software and associated documentation files (the 7// "Software"), to deal in the Software without restriction, including 8// without limitation the rights to use, copy, modify, merge, publish, 9// distribute, sublicense, and/or sell copies of the Software, and to 10// permit persons to whom the Software is furnished to do so, subject to 11// the following conditions: 12// 13// The above copyright notice and this permission notice shall be 14// included in all copies or substantial portions of the Software. 15// 16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 24package proto 25 26import ( 27 "fmt" 28 "io" 29) 30 31// Formatter visits a Proto and writes formatted source. 32type Formatter struct { 33 w io.Writer 34 indentSeparator string 35 indentLevel int 36 lastStmt string 37 lastLevel int 38} 39 40// NewFormatter returns a new Formatter. Only the indentation separator is configurable. 41func NewFormatter(writer io.Writer, indentSeparator string) *Formatter { 42 return &Formatter{w: writer, indentSeparator: indentSeparator} 43} 44 45// Format visits all proto elements and writes formatted source. 46func (f *Formatter) Format(p *Proto) { 47 for _, each := range p.Elements { 48 each.Accept(f) 49 } 50} 51 52// VisitComment formats a Comment and writes a newline. 53func (f *Formatter) VisitComment(c *Comment) { 54 f.printComment(c) 55 f.nl() 56} 57 58// VisitEnum formats a Enum. 59func (f *Formatter) VisitEnum(e *Enum) { 60 f.begin("enum", e) 61 fmt.Fprintf(f.w, "enum %s {", e.Name) 62 if len(e.Elements) > 0 { 63 f.nl() 64 f.level(1) 65 f.printAsGroups(e.Elements) 66 f.indent(-1) 67 } 68 io.WriteString(f.w, "}\n") 69 f.end("enum") 70} 71 72// VisitEnumField formats a EnumField. 73func (f *Formatter) VisitEnumField(e *EnumField) { 74 f.printAsGroups([]Visitee{e}) 75} 76 77// VisitImport formats a Import. 78func (f *Formatter) VisitImport(i *Import) { 79 f.printAsGroups([]Visitee{i}) 80} 81 82// VisitMessage formats a Message. 83func (f *Formatter) VisitMessage(m *Message) { 84 f.begin("message", m) 85 if m.IsExtend { 86 fmt.Fprintf(f.w, "extend ") 87 } else { 88 fmt.Fprintf(f.w, "message ") 89 } 90 fmt.Fprintf(f.w, "%s {", m.Name) 91 if len(m.Elements) > 0 { 92 f.nl() 93 f.level(1) 94 f.printAsGroups(m.Elements) 95 f.indent(-1) 96 } 97 io.WriteString(f.w, "}\n") 98 f.end("message") 99} 100 101// VisitOption formats a Option. 102func (f *Formatter) VisitOption(o *Option) { 103 f.begin("option", o) 104 fmt.Fprintf(f.w, "option %s = ", o.Name) 105 if o.AggregatedConstants != nil { 106 fmt.Fprintf(f.w, "{\n") 107 f.level(1) 108 for _, each := range o.AggregatedConstants { 109 f.indent(0) 110 fmt.Fprintf(f.w, "%s: %s\n", each.Name, each.Literal.String()) 111 } 112 f.indent(-1) 113 fmt.Fprintf(f.w, "}") 114 } else { 115 // TODO printAs groups with fixed length 116 fmt.Fprintf(f.w, o.Constant.String()) 117 } 118 fmt.Fprintf(f.w, ";") 119 if o.InlineComment != nil { 120 fmt.Fprintf(f.w, " //%s", o.InlineComment.Message()) 121 } 122 f.nl() 123} 124 125// VisitPackage formats a Package. 126func (f *Formatter) VisitPackage(p *Package) { 127 f.nl() 128 f.printAsGroups([]Visitee{p}) 129} 130 131// VisitService formats a Service. 132func (f *Formatter) VisitService(s *Service) { 133 f.begin("service", s) 134 fmt.Fprintf(f.w, "service %s {", s.Name) 135 if len(s.Elements) > 0 { 136 f.nl() 137 f.level(1) 138 f.printAsGroups(s.Elements) 139 f.indent(-1) 140 } 141 io.WriteString(f.w, "}\n") 142 f.end("service") 143} 144 145// VisitSyntax formats a Syntax. 146func (f *Formatter) VisitSyntax(s *Syntax) { 147 f.begin("syntax", s) 148 fmt.Fprintf(f.w, "syntax = %q", s.Value) 149 f.endWithComment(s.InlineComment) 150} 151 152// VisitOneof formats a Oneof. 153func (f *Formatter) VisitOneof(o *Oneof) { 154 f.begin("oneof", o) 155 fmt.Fprintf(f.w, "oneof %s {", o.Name) 156 if len(o.Elements) > 0 { 157 f.nl() 158 f.level(1) 159 f.printAsGroups(o.Elements) 160 f.indent(-1) 161 } 162 io.WriteString(f.w, "}\n") 163 f.end("oneof") 164} 165 166// VisitOneofField formats a OneofField. 167func (f *Formatter) VisitOneofField(o *OneOfField) { 168 f.printAsGroups([]Visitee{o}) 169} 170 171// VisitReserved formats a Reserved. 172func (f *Formatter) VisitReserved(r *Reserved) { 173 f.begin("reserved", r) 174 io.WriteString(f.w, "reserved ") 175 if len(r.Ranges) > 0 { 176 for i, each := range r.Ranges { 177 if i > 0 { 178 io.WriteString(f.w, ", ") 179 } 180 fmt.Fprintf(f.w, "%s", each.String()) 181 } 182 } else { 183 for i, each := range r.FieldNames { 184 if i > 0 { 185 io.WriteString(f.w, ", ") 186 } 187 fmt.Fprintf(f.w, "%q", each) 188 } 189 } 190 f.endWithComment(r.InlineComment) 191} 192 193// VisitRPC formats a RPC. 194func (f *Formatter) VisitRPC(r *RPC) { 195 f.printAsGroups([]Visitee{r}) 196} 197 198// VisitMapField formats a MapField. 199func (f *Formatter) VisitMapField(m *MapField) { 200 f.printAsGroups([]Visitee{m}) 201} 202 203// VisitNormalField formats a NormalField. 204func (f *Formatter) VisitNormalField(f1 *NormalField) { 205 f.printAsGroups([]Visitee{f1}) 206} 207 208// VisitGroup formats a proto2 Group. 209func (f *Formatter) VisitGroup(g *Group) { 210 f.begin("group", g) 211 if g.Optional { 212 io.WriteString(f.w, "optional ") 213 } 214 fmt.Fprintf(f.w, "group %s = %d {", g.Name, g.Sequence) 215 if len(g.Elements) > 0 { 216 f.nl() 217 f.level(1) 218 f.printAsGroups(g.Elements) 219 f.indent(-1) 220 } 221 io.WriteString(f.w, "}\n") 222 f.end("group") 223} 224 225// VisitExtensions formats a proto2 Extensions. 226func (f *Formatter) VisitExtensions(e *Extensions) { 227 f.begin("extensions", e) 228 io.WriteString(f.w, "extensions ") 229 for i, each := range e.Ranges { 230 if i > 0 { 231 io.WriteString(f.w, ", ") 232 } 233 fmt.Fprintf(f.w, "%s", each.String()) 234 } 235 f.endWithComment(e.InlineComment) 236} 237