1package pgs
2
3import (
4	"github.com/golang/protobuf/proto"
5	"github.com/golang/protobuf/protoc-gen-go/descriptor"
6)
7
8// File describes the contents of a single proto file.
9type File interface {
10	ParentEntity
11
12	// InputPath returns the input FilePath. This is equivalent to the value
13	// returned by Name.
14	InputPath() FilePath
15
16	// Descriptor returns the underlying descriptor for the proto file
17	Descriptor() *descriptor.FileDescriptorProto
18
19	// Services returns the services from this proto file.
20	Services() []Service
21
22	// SyntaxSourceCodeInfo returns the comment info attached to the `syntax`
23	// stanza of the file. This method is an alias of the SourceCodeInfo method.
24	SyntaxSourceCodeInfo() SourceCodeInfo
25
26	// PackageSourceCodeInfo returns the comment info attached to the `package`
27	// stanza of the file.
28	PackageSourceCodeInfo() SourceCodeInfo
29
30	setPackage(p Package)
31
32	addService(s Service)
33
34	addPackageSourceCodeInfo(info SourceCodeInfo)
35}
36
37type file struct {
38	desc                    *descriptor.FileDescriptorProto
39	fqn                     string
40	pkg                     Package
41	enums                   []Enum
42	defExts                 []Extension
43	msgs                    []Message
44	srvs                    []Service
45	buildTarget             bool
46	syntaxInfo, packageInfo SourceCodeInfo
47}
48
49func (f *file) Name() Name                                  { return Name(f.desc.GetName()) }
50func (f *file) FullyQualifiedName() string                  { return f.fqn }
51func (f *file) Syntax() Syntax                              { return Syntax(f.desc.GetSyntax()) }
52func (f *file) Package() Package                            { return f.pkg }
53func (f *file) File() File                                  { return f }
54func (f *file) BuildTarget() bool                           { return f.buildTarget }
55func (f *file) Descriptor() *descriptor.FileDescriptorProto { return f.desc }
56func (f *file) InputPath() FilePath                         { return FilePath(f.Name().String()) }
57func (f *file) MapEntries() (me []Message)                  { return nil }
58func (f *file) SourceCodeInfo() SourceCodeInfo              { return f.SyntaxSourceCodeInfo() }
59func (f *file) SyntaxSourceCodeInfo() SourceCodeInfo        { return f.syntaxInfo }
60func (f *file) PackageSourceCodeInfo() SourceCodeInfo       { return f.packageInfo }
61
62func (f *file) Enums() []Enum {
63	return f.enums
64}
65
66func (f *file) AllEnums() []Enum {
67	es := f.Enums()
68	for _, m := range f.msgs {
69		es = append(es, m.AllEnums()...)
70	}
71	return es
72}
73
74func (f *file) Messages() []Message {
75	return f.msgs
76}
77
78func (f *file) AllMessages() []Message {
79	msgs := f.Messages()
80	for _, m := range f.msgs {
81		msgs = append(msgs, m.AllMessages()...)
82	}
83	return msgs
84}
85
86func (f *file) Services() []Service {
87	return f.srvs
88}
89
90func (f *file) Imports() (i []File) {
91	// Mapping for avoiding duplicate entries
92	importMap := make(map[string]File, len(f.AllMessages())+len(f.srvs))
93	for _, m := range f.AllMessages() {
94		for _, imp := range m.Imports() {
95			importMap[imp.File().Name().String()] = imp
96		}
97	}
98	for _, s := range f.srvs {
99		for _, imp := range s.Imports() {
100			importMap[imp.File().Name().String()] = imp
101		}
102	}
103	for _, imp := range importMap {
104		i = append(i, imp)
105	}
106	return
107}
108
109func (f *file) Extension(desc *proto.ExtensionDesc, ext interface{}) (bool, error) {
110	return extension(f.desc.GetOptions(), desc, &ext)
111}
112
113func (f *file) DefinedExtensions() []Extension {
114	return f.defExts
115}
116
117func (f *file) accept(v Visitor) (err error) {
118	if v == nil {
119		return nil
120	}
121
122	if v, err = v.VisitFile(f); err != nil || v == nil {
123		return
124	}
125
126	for _, e := range f.enums {
127		if err = e.accept(v); err != nil {
128			return
129		}
130	}
131
132	for _, m := range f.msgs {
133		if err = m.accept(v); err != nil {
134			return
135		}
136	}
137
138	for _, s := range f.srvs {
139		if err = s.accept(v); err != nil {
140			return
141		}
142	}
143
144	for _, ext := range f.defExts {
145		if err = ext.accept(v); err != nil {
146			return
147		}
148	}
149
150	return
151}
152
153func (f *file) addDefExtension(ext Extension) {
154	f.defExts = append(f.defExts, ext)
155}
156
157func (f *file) setPackage(pkg Package) { f.pkg = pkg }
158
159func (f *file) addEnum(e Enum) {
160	e.setParent(f)
161	f.enums = append(f.enums, e)
162}
163
164func (f *file) addMessage(m Message) {
165	m.setParent(f)
166	f.msgs = append(f.msgs, m)
167}
168
169func (f *file) addService(s Service) {
170	s.setFile(f)
171	f.srvs = append(f.srvs, s)
172}
173
174func (f *file) addMapEntry(m Message) { panic("cannot add map entry directly to file") }
175
176func (f *file) childAtPath(path []int32) Entity {
177	switch {
178	case len(path) == 0:
179		return f
180	case len(path)%2 == 1: // all declaration paths are multiples of two
181		return nil
182	}
183
184	var child Entity
185	switch path[0] {
186	case messageTypePath:
187		child = f.msgs[path[1]]
188	case enumTypePath:
189		child = f.enums[path[1]]
190	case servicePath:
191		child = f.srvs[path[1]]
192	default:
193		return nil
194	}
195
196	return child.childAtPath(path[2:])
197}
198
199func (f *file) addSourceCodeInfo(info SourceCodeInfo) {
200	f.syntaxInfo = info
201}
202
203func (f *file) addPackageSourceCodeInfo(info SourceCodeInfo) {
204	f.packageInfo = info
205}
206
207var _ File = (*file)(nil)
208