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	"strconv"
29	"text/scanner"
30)
31
32// Field is an abstract message field.
33type Field struct {
34	Position      scanner.Position
35	Comment       *Comment
36	Name          string
37	Type          string
38	Sequence      int
39	Options       []*Option
40	InlineComment *Comment
41}
42
43// inlineComment is part of commentInliner.
44func (f *Field) inlineComment(c *Comment) {
45	f.InlineComment = c
46}
47
48// NormalField represents a field in a Message.
49type NormalField struct {
50	*Field
51	Repeated bool
52	Optional bool // proto2
53	Required bool // proto2
54}
55
56func newNormalField() *NormalField { return &NormalField{Field: new(Field)} }
57
58// Accept dispatches the call to the visitor.
59func (f *NormalField) Accept(v Visitor) {
60	v.VisitNormalField(f)
61}
62
63// Doc is part of Documented
64func (f *NormalField) Doc() *Comment {
65	return f.Comment
66}
67
68// columns returns printable source tokens
69func (f *NormalField) columns() (cols []aligned) {
70	if f.Repeated {
71		cols = append(cols, leftAligned("repeated "))
72	} else {
73		cols = append(cols, alignedEmpty)
74	}
75	if f.Optional {
76		cols = append(cols, leftAligned("optional "))
77	} else {
78		cols = append(cols, alignedEmpty)
79	}
80	cols = append(cols, rightAligned(f.Type), alignedSpace, leftAligned(f.Name), alignedEquals, rightAligned(strconv.Itoa(f.Sequence)))
81	if len(f.Options) > 0 {
82		cols = append(cols, leftAligned(" ["))
83		for i, each := range f.Options {
84			if i > 0 {
85				cols = append(cols, alignedComma)
86			}
87			cols = append(cols, each.keyValuePair(true)...)
88		}
89		cols = append(cols, leftAligned("]"))
90	}
91	cols = append(cols, alignedSemicolon)
92	if f.InlineComment != nil {
93		cols = append(cols, f.InlineComment.alignedInlinePrefix(), notAligned(f.InlineComment.Message()))
94	}
95	return
96}
97
98// parse expects:
99// [ "repeated" | "optional" ] type fieldName "=" fieldNumber [ "[" fieldOptions "]" ] ";"
100func (f *NormalField) parse(p *Parser) error {
101	for {
102		_, tok, lit := p.nextIdentifier()
103		switch tok {
104		case tREPEATED:
105			f.Repeated = true
106			return f.parse(p)
107		case tOPTIONAL: // proto2
108			f.Optional = true
109			return f.parse(p)
110		case tIDENT:
111			f.Type = lit
112			return parseFieldAfterType(f.Field, p)
113		default:
114			goto done
115		}
116	}
117done:
118	return nil
119}
120
121// parseFieldAfterType expects:
122// fieldName "=" fieldNumber [ "[" fieldOptions "]" ] ";
123func parseFieldAfterType(f *Field, p *Parser) error {
124	pos, tok, lit := p.next()
125	if tok != tIDENT {
126		if !isKeyword(tok) {
127			return p.unexpected(lit, "field identifier", f)
128		}
129	}
130	f.Name = lit
131	pos, tok, lit = p.next()
132	if tok != tEQUALS {
133		return p.unexpected(lit, "field =", f)
134	}
135	i, err := p.nextInteger()
136	if err != nil {
137		return p.unexpected(lit, "field sequence number", f)
138	}
139	f.Sequence = i
140	// see if there are options
141	pos, tok, lit = p.next()
142	if tLEFTSQUARE != tok {
143		return nil
144	}
145	// consume options
146	for {
147		o := new(Option)
148		o.Position = pos
149		o.IsEmbedded = true
150		err := o.parse(p)
151		if err != nil {
152			return err
153		}
154		f.Options = append(f.Options, o)
155
156		pos, tok, lit = p.next()
157		if tRIGHTSQUARE == tok {
158			break
159		}
160		if tCOMMA != tok {
161			return p.unexpected(lit, "option ,", o)
162		}
163	}
164	return nil
165}
166
167func (n *NormalField) String() string { return fmt.Sprintf("<field %s=%d>", n.Name, n.Sequence) }
168
169// MapField represents a map entry in a message.
170type MapField struct {
171	*Field
172	KeyType string
173}
174
175func newMapField() *MapField { return &MapField{Field: new(Field)} }
176
177// Accept dispatches the call to the visitor.
178func (f *MapField) Accept(v Visitor) {
179	v.VisitMapField(f)
180}
181
182// columns returns printable source tokens
183func (f *MapField) columns() (cols []aligned) {
184	cols = append(cols,
185		notAligned("map <"),
186		rightAligned(f.KeyType),
187		notAligned(","),
188		leftAligned(f.Type),
189		notAligned("> "),
190		rightAligned(f.Name),
191		alignedEquals,
192		rightAligned(strconv.Itoa(f.Sequence)))
193	if len(f.Options) > 0 {
194		cols = append(cols, leftAligned(" ["))
195		for i, each := range f.Options {
196			if i > 0 {
197				cols = append(cols, alignedComma)
198			}
199			cols = append(cols, each.keyValuePair(true)...)
200		}
201		cols = append(cols, leftAligned("]"))
202	}
203	cols = append(cols, alignedSemicolon)
204	if f.InlineComment != nil {
205		cols = append(cols, f.InlineComment.alignedInlinePrefix(), notAligned(f.InlineComment.Message()))
206	}
207	return
208}
209
210// parse expects:
211// mapField = "map" "<" keyType "," type ">" mapName "=" fieldNumber [ "[" fieldOptions "]" ] ";"
212// keyType = "int32" | "int64" | "uint32" | "uint64" | "sint32" | "sint64" |
213//           "fixed32" | "fixed64" | "sfixed32" | "sfixed64" | "bool" | "string"
214func (f *MapField) parse(p *Parser) error {
215	_, tok, lit := p.next()
216	if tLESS != tok {
217		return p.unexpected(lit, "map keyType <", f)
218	}
219	_, tok, lit = p.next()
220	if tIDENT != tok {
221		return p.unexpected(lit, "map identifier", f)
222	}
223	f.KeyType = lit
224	_, tok, lit = p.next()
225	if tCOMMA != tok {
226		return p.unexpected(lit, "map type separator ,", f)
227	}
228	_, tok, lit = p.nextIdentifier()
229	if tIDENT != tok {
230		return p.unexpected(lit, "map valueType identifier", f)
231	}
232	f.Type = lit
233	_, tok, lit = p.next()
234	if tGREATER != tok {
235		return p.unexpected(lit, "map valueType >", f)
236	}
237	return parseFieldAfterType(f.Field, p)
238}
239