protostar 0.1.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +60 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +647 -0
  5. data/THIRD_PARTY_NOTICES.md +61 -0
  6. data/exe/protoc-gen-protostar +7 -0
  7. data/exe/protostar +7 -0
  8. data/lib/protostar/builder/generator.rb +71 -0
  9. data/lib/protostar/builder/sink.rb +35 -0
  10. data/lib/protostar/builder.rb +965 -0
  11. data/lib/protostar/cli.rb +267 -0
  12. data/lib/protostar/codec/decode_generator.rb +199 -0
  13. data/lib/protostar/codec/decoder.rb +296 -0
  14. data/lib/protostar/codec/encode_generator.rb +224 -0
  15. data/lib/protostar/codec/encoder.rb +121 -0
  16. data/lib/protostar/codec/sizer.rb +91 -0
  17. data/lib/protostar/codec.rb +15 -0
  18. data/lib/protostar/compiler/ast.rb +193 -0
  19. data/lib/protostar/compiler/emitters/descriptor_set.rb +57 -0
  20. data/lib/protostar/compiler/emitters/google_ruby.rb +103 -0
  21. data/lib/protostar/compiler/emitters/grpc.rb +175 -0
  22. data/lib/protostar/compiler/emitters/protostar_rbs.rb +252 -0
  23. data/lib/protostar/compiler/emitters/rbs.rb +394 -0
  24. data/lib/protostar/compiler/emitters/ruby.rb +503 -0
  25. data/lib/protostar/compiler/emitters.rb +268 -0
  26. data/lib/protostar/compiler/include/LICENSE +32 -0
  27. data/lib/protostar/compiler/include/google/protobuf/any.proto +106 -0
  28. data/lib/protostar/compiler/include/google/protobuf/api.proto +229 -0
  29. data/lib/protostar/compiler/include/google/protobuf/compiler/plugin.proto +180 -0
  30. data/lib/protostar/compiler/include/google/protobuf/cpp_features.proto +87 -0
  31. data/lib/protostar/compiler/include/google/protobuf/descriptor.proto +1532 -0
  32. data/lib/protostar/compiler/include/google/protobuf/duration.proto +115 -0
  33. data/lib/protostar/compiler/include/google/protobuf/empty.proto +51 -0
  34. data/lib/protostar/compiler/include/google/protobuf/field_mask.proto +243 -0
  35. data/lib/protostar/compiler/include/google/protobuf/java_features.proto +132 -0
  36. data/lib/protostar/compiler/include/google/protobuf/source_context.proto +48 -0
  37. data/lib/protostar/compiler/include/google/protobuf/struct.proto +111 -0
  38. data/lib/protostar/compiler/include/google/protobuf/timestamp.proto +145 -0
  39. data/lib/protostar/compiler/include/google/protobuf/type.proto +217 -0
  40. data/lib/protostar/compiler/include/google/protobuf/wrappers.proto +157 -0
  41. data/lib/protostar/compiler/lexer.rb +659 -0
  42. data/lib/protostar/compiler/linker.rb +894 -0
  43. data/lib/protostar/compiler/option_interpreter.rb +363 -0
  44. data/lib/protostar/compiler/parser.rb +1696 -0
  45. data/lib/protostar/compiler/retention.rb +104 -0
  46. data/lib/protostar/compiler/run.rb +184 -0
  47. data/lib/protostar/compiler/source_info.rb +161 -0
  48. data/lib/protostar/compiler/symbol_table.rb +223 -0
  49. data/lib/protostar/compiler/text_format.rb +340 -0
  50. data/lib/protostar/compiler/validator.rb +944 -0
  51. data/lib/protostar/compiler.rb +304 -0
  52. data/lib/protostar/diagnostics/codes.rb +1307 -0
  53. data/lib/protostar/diagnostics/formats.rb +64 -0
  54. data/lib/protostar/diagnostics/renderer.rb +240 -0
  55. data/lib/protostar/diagnostics/source.rb +153 -0
  56. data/lib/protostar/diagnostics.rb +117 -0
  57. data/lib/protostar/dsl.rb +367 -0
  58. data/lib/protostar/enum.rb +70 -0
  59. data/lib/protostar/errors.rb +23 -0
  60. data/lib/protostar/google/protobuf/any_pb.rb +90 -0
  61. data/lib/protostar/google/protobuf/api_pb.rb +199 -0
  62. data/lib/protostar/google/protobuf/compiler/plugin_pb.rb +168 -0
  63. data/lib/protostar/google/protobuf/descriptor_pb.rb +1239 -0
  64. data/lib/protostar/google/protobuf/duration_pb.rb +99 -0
  65. data/lib/protostar/google/protobuf/empty_pb.rb +36 -0
  66. data/lib/protostar/google/protobuf/field_mask_pb.rb +228 -0
  67. data/lib/protostar/google/protobuf/source_context_pb.rb +33 -0
  68. data/lib/protostar/google/protobuf/struct_pb.rb +91 -0
  69. data/lib/protostar/google/protobuf/timestamp_pb.rb +129 -0
  70. data/lib/protostar/google/protobuf/type_pb.rb +202 -0
  71. data/lib/protostar/google/protobuf/wrappers_pb.rb +125 -0
  72. data/lib/protostar/google_protobuf_shim.rb +24 -0
  73. data/lib/protostar/json/encoder.rb +340 -0
  74. data/lib/protostar/json/errors.rb +34 -0
  75. data/lib/protostar/json/parser.rb +437 -0
  76. data/lib/protostar/json/path.rb +39 -0
  77. data/lib/protostar/json.rb +95 -0
  78. data/lib/protostar/lint/command.rb +197 -0
  79. data/lib/protostar/lint/config.rb +98 -0
  80. data/lib/protostar/lint/context.rb +126 -0
  81. data/lib/protostar/lint/directives.rb +56 -0
  82. data/lib/protostar/lint/fixer.rb +165 -0
  83. data/lib/protostar/lint/naming.rb +54 -0
  84. data/lib/protostar/lint/rule.rb +120 -0
  85. data/lib/protostar/lint/rules/comment_enum.rb +31 -0
  86. data/lib/protostar/lint/rules/comment_enum_value.rb +32 -0
  87. data/lib/protostar/lint/rules/comment_field.rb +33 -0
  88. data/lib/protostar/lint/rules/comment_message.rb +31 -0
  89. data/lib/protostar/lint/rules/comment_rpc.rb +31 -0
  90. data/lib/protostar/lint/rules/comment_service.rb +31 -0
  91. data/lib/protostar/lint/rules/edition_recommended.rb +31 -0
  92. data/lib/protostar/lint/rules/enum_first_value_zero.rb +34 -0
  93. data/lib/protostar/lint/rules/enum_pascal_case.rb +25 -0
  94. data/lib/protostar/lint/rules/enum_value_prefix.rb +38 -0
  95. data/lib/protostar/lint/rules/enum_value_upper_snake_case.rb +28 -0
  96. data/lib/protostar/lint/rules/enum_zero_value_suffix.rb +41 -0
  97. data/lib/protostar/lint/rules/field_lower_snake_case.rb +34 -0
  98. data/lib/protostar/lint/rules/field_not_required.rb +39 -0
  99. data/lib/protostar/lint/rules/field_number_reuse_risk.rb +45 -0
  100. data/lib/protostar/lint/rules/field_numbers_hot_range.rb +44 -0
  101. data/lib/protostar/lint/rules/file_lower_snake_case.rb +27 -0
  102. data/lib/protostar/lint/rules/import_no_public.rb +29 -0
  103. data/lib/protostar/lint/rules/import_no_weak.rb +28 -0
  104. data/lib/protostar/lint/rules/import_used.rb +33 -0
  105. data/lib/protostar/lint/rules/message_pascal_case.rb +27 -0
  106. data/lib/protostar/lint/rules/no_deprecated_features.rb +50 -0
  107. data/lib/protostar/lint/rules/no_groups.rb +34 -0
  108. data/lib/protostar/lint/rules/oneof_lower_snake_case.rb +27 -0
  109. data/lib/protostar/lint/rules/package_defined.rb +28 -0
  110. data/lib/protostar/lint/rules/package_directory_match.rb +33 -0
  111. data/lib/protostar/lint/rules/package_lower_snake_case.rb +28 -0
  112. data/lib/protostar/lint/rules/package_same_directory.rb +39 -0
  113. data/lib/protostar/lint/rules/package_version_suffix.rb +31 -0
  114. data/lib/protostar/lint/rules/reserved_names_with_numbers.rb +44 -0
  115. data/lib/protostar/lint/rules/rpc_pascal_case.rb +25 -0
  116. data/lib/protostar/lint/rules/rpc_request_response_unique.rb +40 -0
  117. data/lib/protostar/lint/rules/rpc_request_standard_name.rb +32 -0
  118. data/lib/protostar/lint/rules/rpc_response_standard_name.rb +31 -0
  119. data/lib/protostar/lint/rules/service_pascal_case.rb +25 -0
  120. data/lib/protostar/lint/rules/service_suffix.rb +28 -0
  121. data/lib/protostar/lint/rules/syntax_specified.rb +35 -0
  122. data/lib/protostar/lint/runner.rb +135 -0
  123. data/lib/protostar/lint.rb +122 -0
  124. data/lib/protostar/message/accessors.rb +86 -0
  125. data/lib/protostar/message/coercion.rb +171 -0
  126. data/lib/protostar/message/field_info.rb +145 -0
  127. data/lib/protostar/message/reflection.rb +149 -0
  128. data/lib/protostar/message.rb +572 -0
  129. data/lib/protostar/plugin.rb +94 -0
  130. data/lib/protostar/schema/default_value.rb +113 -0
  131. data/lib/protostar/schema/descriptor.rb +41 -0
  132. data/lib/protostar/schema/draft/copy.rb +88 -0
  133. data/lib/protostar/schema/draft/resolution.rb +177 -0
  134. data/lib/protostar/schema/draft.rb +779 -0
  135. data/lib/protostar/schema/enum.rb +106 -0
  136. data/lib/protostar/schema/extension_range.rb +37 -0
  137. data/lib/protostar/schema/features.rb +153 -0
  138. data/lib/protostar/schema/field.rb +201 -0
  139. data/lib/protostar/schema/file.rb +217 -0
  140. data/lib/protostar/schema/freezer.rb +61 -0
  141. data/lib/protostar/schema/message.rb +104 -0
  142. data/lib/protostar/schema/oneof.rb +38 -0
  143. data/lib/protostar/schema/options.rb +367 -0
  144. data/lib/protostar/schema/pool.rb +179 -0
  145. data/lib/protostar/schema/proto.rb +576 -0
  146. data/lib/protostar/schema/resolver.rb +118 -0
  147. data/lib/protostar/schema/service.rb +89 -0
  148. data/lib/protostar/schema/source_info.rb +53 -0
  149. data/lib/protostar/schema.rb +65 -0
  150. data/lib/protostar/short_names/any.rb +7 -0
  151. data/lib/protostar/short_names/duration.rb +7 -0
  152. data/lib/protostar/short_names/empty.rb +7 -0
  153. data/lib/protostar/short_names/field_mask.rb +7 -0
  154. data/lib/protostar/short_names/struct.rb +9 -0
  155. data/lib/protostar/short_names/timestamp.rb +7 -0
  156. data/lib/protostar/short_names/wrappers.rb +15 -0
  157. data/lib/protostar/text/extensions.rb +114 -0
  158. data/lib/protostar/text/parser.rb +439 -0
  159. data/lib/protostar/text/printer.rb +304 -0
  160. data/lib/protostar/text/tokenizer.rb +116 -0
  161. data/lib/protostar/text.rb +67 -0
  162. data/lib/protostar/types.rb +152 -0
  163. data/lib/protostar/unknown_field.rb +58 -0
  164. data/lib/protostar/version.rb +5 -0
  165. data/lib/protostar/view/generator.rb +298 -0
  166. data/lib/protostar/view/map.rb +194 -0
  167. data/lib/protostar/view/repeated.rb +143 -0
  168. data/lib/protostar/view/scan.rb +183 -0
  169. data/lib/protostar/view.rb +283 -0
  170. data/lib/protostar/well_known/any.rb +52 -0
  171. data/lib/protostar/well_known/duration.rb +53 -0
  172. data/lib/protostar/well_known/list_value.rb +63 -0
  173. data/lib/protostar/well_known/struct.rb +55 -0
  174. data/lib/protostar/well_known/timestamp.rb +64 -0
  175. data/lib/protostar/well_known/value.rb +60 -0
  176. data/lib/protostar/well_known.rb +38 -0
  177. data/lib/protostar/wire/io_source.rb +40 -0
  178. data/lib/protostar/wire/reader.rb +254 -0
  179. data/lib/protostar/wire/writer.rb +201 -0
  180. data/lib/protostar/wire.rb +96 -0
  181. data/lib/protostar.rb +95 -0
  182. data/shims/google-protobuf/lib/google/protobuf/any_pb.rb +17 -0
  183. data/shims/google-protobuf/lib/google/protobuf/api_pb.rb +22 -0
  184. data/shims/google-protobuf/lib/google/protobuf/compiler/plugin_pb.rb +25 -0
  185. data/shims/google-protobuf/lib/google/protobuf/descriptor_pb.rb +72 -0
  186. data/shims/google-protobuf/lib/google/protobuf/descriptor_pool.rb +164 -0
  187. data/shims/google-protobuf/lib/google/protobuf/descriptors.rb +553 -0
  188. data/shims/google-protobuf/lib/google/protobuf/duration_pb.rb +17 -0
  189. data/shims/google-protobuf/lib/google/protobuf/empty_pb.rb +17 -0
  190. data/shims/google-protobuf/lib/google/protobuf/field_mask_pb.rb +17 -0
  191. data/shims/google-protobuf/lib/google/protobuf/internal/convert.rb +180 -0
  192. data/shims/google-protobuf/lib/google/protobuf/internal/extensions.rb +154 -0
  193. data/shims/google-protobuf/lib/google/protobuf/internal/json.rb +104 -0
  194. data/shims/google-protobuf/lib/google/protobuf/internal/well_known.rb +140 -0
  195. data/shims/google-protobuf/lib/google/protobuf/map.rb +216 -0
  196. data/shims/google-protobuf/lib/google/protobuf/message.rb +544 -0
  197. data/shims/google-protobuf/lib/google/protobuf/message_exts.rb +41 -0
  198. data/shims/google-protobuf/lib/google/protobuf/plugin_pb.rb +4 -0
  199. data/shims/google-protobuf/lib/google/protobuf/repeated_field.rb +366 -0
  200. data/shims/google-protobuf/lib/google/protobuf/source_context_pb.rb +17 -0
  201. data/shims/google-protobuf/lib/google/protobuf/struct_pb.rb +20 -0
  202. data/shims/google-protobuf/lib/google/protobuf/timestamp_pb.rb +17 -0
  203. data/shims/google-protobuf/lib/google/protobuf/type_pb.rb +27 -0
  204. data/shims/google-protobuf/lib/google/protobuf/well_known_types.rb +37 -0
  205. data/shims/google-protobuf/lib/google/protobuf/wrappers_pb.rb +25 -0
  206. data/shims/google-protobuf/lib/google/protobuf.rb +107 -0
  207. data/shims/google-protobuf/lib/google/protobuf_ffi.rb +5 -0
  208. data/shims/google-protobuf/lib/google/protobuf_native.rb +5 -0
  209. data/sig/protostar/google/protobuf/any_pb.rbs +77 -0
  210. data/sig/protostar/google/protobuf/api_pb.rbs +242 -0
  211. data/sig/protostar/google/protobuf/compiler/plugin_pb.rbs +264 -0
  212. data/sig/protostar/google/protobuf/descriptor_pb.rbs +2220 -0
  213. data/sig/protostar/google/protobuf/duration_pb.rbs +82 -0
  214. data/sig/protostar/google/protobuf/empty_pb.rbs +18 -0
  215. data/sig/protostar/google/protobuf/field_mask_pb.rbs +170 -0
  216. data/sig/protostar/google/protobuf/source_context_pb.rbs +24 -0
  217. data/sig/protostar/google/protobuf/struct_pb.rbs +135 -0
  218. data/sig/protostar/google/protobuf/timestamp_pb.rbs +102 -0
  219. data/sig/protostar/google/protobuf/type_pb.rbs +304 -0
  220. data/sig/protostar/google/protobuf/wrappers_pb.rbs +169 -0
  221. data/sig/protostar/lint.rbs +216 -0
  222. data/sig/protostar.rbs +326 -0
  223. metadata +366 -0
@@ -0,0 +1,304 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protostar
4
+ # A pure-Ruby protoc front end: turns `.proto` source into
5
+ # {Protostar::Schema::File}s whose `to_proto` is what protoc 36.2 puts in a
6
+ # `FileDescriptorProto`, and rejects what protoc rejects with protoc's
7
+ # `file:line:col: message` diagnostics.
8
+ #
9
+ # compiler = Protostar::Compiler.new(include_paths: ["proto"])
10
+ # files = compiler.compile(["foo/bar.proto"], source_info: true)
11
+ class Compiler
12
+ # Where the bundled `google/protobuf/*.proto` imports live
13
+ INCLUDE_DIR = ::File.expand_path("compiler/include", __dir__)
14
+
15
+ autoload :Lexer, ::File.expand_path("compiler/lexer", __dir__)
16
+ autoload :AST, ::File.expand_path("compiler/ast", __dir__)
17
+ autoload :SourceInfo, ::File.expand_path("compiler/source_info", __dir__)
18
+ autoload :Parser, ::File.expand_path("compiler/parser", __dir__)
19
+ autoload :SymbolTable, ::File.expand_path("compiler/symbol_table", __dir__)
20
+ autoload :Linker, ::File.expand_path("compiler/linker", __dir__)
21
+ autoload :OptionInterpreter, ::File.expand_path("compiler/option_interpreter", __dir__)
22
+ autoload :TextFormat, ::File.expand_path("compiler/text_format", __dir__)
23
+ autoload :Validator, ::File.expand_path("compiler/validator", __dir__)
24
+ autoload :Retention, ::File.expand_path("compiler/retention", __dir__)
25
+ autoload :Run, ::File.expand_path("compiler/run", __dir__)
26
+ autoload :Emitters, ::File.expand_path("compiler/emitters", __dir__)
27
+
28
+ # One error or warning, with protoc's 1-based line and column (nil when
29
+ # protoc reports none). {#to_s} and {#format} print it as protoc does; the
30
+ # rest is for {Diagnostics::Renderer}, which prints it as rustc would.
31
+ class Diagnostic
32
+ # @return [String] the file name as imported
33
+ attr_reader :file
34
+ # @return [Integer, nil]
35
+ attr_reader :line
36
+ # @return [Integer, nil]
37
+ attr_reader :column
38
+ # @return [String]
39
+ attr_reader :message
40
+ # @return [Symbol] :error or :warning
41
+ attr_reader :severity
42
+ # @return [String, nil] the file on disk, spelled as protoc prints it: the include
43
+ # path as given (canonicalized) joined with {#file}; nil for in-memory and bundled files
44
+ attr_reader :path
45
+ # @return [Array<Diagnostics::Span>] the primary span first, then related ones; empty
46
+ # when the compiler knows only {#line} and {#column}, or nothing
47
+ attr_reader :spans
48
+ # @return [Array<String>] `help:` lines, the concrete fix in words
49
+ attr_reader :help
50
+ # @return [Array<String>] `note:` lines
51
+ attr_reader :notes
52
+ # @return [Diagnostics::Fix, nil] a mechanical fix `protostar lint --fix` can apply
53
+ attr_reader :fix
54
+ # @return [String, nil] the lint rule that found it, e.g. "FIELD_NOT_REQUIRED"
55
+ attr_reader :rule
56
+
57
+ def initialize(file, line, column, message, severity = :error, path: nil, spans: [], code: nil, title: nil,
58
+ help: [], notes: [], fix: nil, rule: nil)
59
+ @file = file
60
+ @line = line
61
+ @column = column
62
+ @message = message
63
+ @severity = severity
64
+ @path = path
65
+ @spans = spans.freeze
66
+ @code = code
67
+ @title = title
68
+ @help = help.freeze
69
+ @notes = notes.freeze
70
+ @fix = fix
71
+ @rule = rule
72
+ freeze
73
+ end
74
+
75
+ # @return [String, nil] the stable code `protostar lint --explain` knows, e.g. "L0201";
76
+ # for the compiler's messages, looked up in {Diagnostics::Codes}
77
+ def code
78
+ @code || Diagnostics::Codes.lookup(@message)&.code
79
+ end
80
+
81
+ # @return [String] the headline the renderer prints: a short rewording where the
82
+ # catalogue has one, else {#message}
83
+ def title
84
+ @title || @message
85
+ end
86
+
87
+ # @return [Diagnostic] a copy with what {Diagnostics::Codes} knows about the message
88
+ # filled in: code, title, span labels, help and notes
89
+ def annotated
90
+ @code ? self : Diagnostics::Codes.annotate(self)
91
+ end
92
+
93
+ # @return [Diagnostic] a copy with the given attributes replaced
94
+ def with(**changes)
95
+ attrs = {path: @path, spans: @spans, code: @code, title: @title, help: @help, notes: @notes, fix: @fix,
96
+ rule: @rule}.merge(changes)
97
+ Diagnostic.new(@file, @line, @column, @message, @severity, **attrs)
98
+ end
99
+
100
+ def error?
101
+ @severity == :error
102
+ end
103
+
104
+ def warning?
105
+ @severity == :warning
106
+ end
107
+
108
+ # @return [String] as protoc prints it, with the virtual file name
109
+ def to_s
110
+ location = @line ? "#{@file}:#{@line}:#{@column}" : @file
111
+ warning? ? "#{location}: warning: #{@message}" : "#{location}: #{@message}"
112
+ end
113
+
114
+ # @param error_format [Symbol] :gcc or :msvs, protoc's `--error_format`
115
+ # @return [String] as protoc's command line prints it: the path on disk when known
116
+ def format(error_format = :gcc)
117
+ name = @path || @file
118
+ location = if @line.nil?
119
+ name
120
+ elsif error_format == :msvs
121
+ "#{name}(#{@line}) : #{@severity} in column=#{@column}"
122
+ else
123
+ "#{name}:#{@line}:#{@column}"
124
+ end
125
+ warning? ? "#{location}: warning: #{@message}" : "#{location}: #{@message}"
126
+ end
127
+
128
+ def ==(other)
129
+ other.is_a?(Diagnostic) && other.to_s == to_s
130
+ end
131
+ alias_method :eql?, :==
132
+
133
+ def hash
134
+ to_s.hash
135
+ end
136
+
137
+ def inspect
138
+ "#<#{self.class.name} #{self}>"
139
+ end
140
+ end
141
+
142
+ # Raised when compilation fails
143
+ class Error < Protostar::Error
144
+ # @return [Array<Diagnostic>] every error found, in protoc's order
145
+ attr_reader :diagnostics
146
+
147
+ def initialize(diagnostics)
148
+ @diagnostics = diagnostics.freeze
149
+ super(diagnostics.join("\n"))
150
+ end
151
+ end
152
+
153
+ # @return [Schema::Pool] where compiled files go and imports are looked up
154
+ attr_reader :pool
155
+ # @return [Array<String>] directories searched for imports, in order, before the bundled includes
156
+ attr_reader :include_paths
157
+ # @return [Array<Diagnostic>] warnings (and errors) of the last {#compile}
158
+ attr_reader :diagnostics
159
+
160
+ # @param include_paths [Array<String>] directories to resolve `.proto` names against, in order
161
+ # @param pool [Schema::Pool] receives every compiled file; files already in it are reused
162
+ # @param bundled_includes [Boolean] search the bundled `google/protobuf/*.proto` after `include_paths`
163
+ def initialize(include_paths: [], pool: Schema::Pool.new, bundled_includes: true)
164
+ @given_paths = include_paths.map { |p| Compiler.canonical_path(p) }.freeze
165
+ @include_paths = include_paths.map { |p| ::File.expand_path(p) }.freeze
166
+ @include_paths += [INCLUDE_DIR] if bundled_includes
167
+ @pool = pool
168
+ @diagnostics = []
169
+ end
170
+
171
+ # Compiles `.proto` files and everything they import.
172
+ #
173
+ # The pool keeps every option, as protoc's own pool does, so later files
174
+ # are validated against the full declarations. What is returned is what
175
+ # protoc writes with `--descriptor_set_out`: options declared
176
+ # `retention = RETENTION_SOURCE` removed (pass `retain_options: true`
177
+ # for the pool's files as they are).
178
+ #
179
+ # @param names [Array<String>] file names relative to an include path, e.g. "foo/bar.proto"
180
+ # @param source_info [Boolean] keep `source_code_info` in the built files
181
+ # @param retain_options [Boolean] return the pool's files, source-retention options included
182
+ # @param sources [Hash{String => String}] file contents by name, consulted before the include paths
183
+ # @param protoc_cli [Boolean] @api private: false checks like a bare DescriptorPool rather than
184
+ # protoc's command line (no imports loaded from disk, no unused import, naming style or other
185
+ # command-line checks), as protoc's own parser tests do
186
+ # @return [Array<Schema::File>] the named files and their transitive imports, imports first
187
+ # @raise [Error] with every diagnostic when a file cannot be compiled
188
+ def compile(names, source_info: false, retain_options: false, sources: {}, protoc_cli: true)
189
+ run = Run.new(self, names, source_info, sources: sources, protoc_cli: protoc_cli)
190
+ files = run.call
191
+ @diagnostics = run.diagnostics
192
+ raise Error.new(run.diagnostics) if run.failed?
193
+ retain_options ? files : files.map { |f| output_file(f) }
194
+ end
195
+
196
+ # Same as {#compile}, each file as `to_proto.to_h`, for reading
197
+ # @deprecated Goes away in 0.2.0. Use {#compile} and read the {Schema::File}s, or
198
+ # `compile(names).map { |f| f.to_proto.to_h }` for the same Hashes.
199
+ # @return [Array<Hash>]
200
+ def compile_to_hashes(names, source_info: false, retain_options: false, sources: {})
201
+ Protostar.deprecated("Compiler#compile_to_hashes", "compile(names).map { |f| f.to_proto.to_h }")
202
+ compile(names, source_info: source_info, retain_options: retain_options, sources: sources).map(&:to_h)
203
+ end
204
+
205
+ # @param name [String] a virtual file name
206
+ # @return [String, nil] the path it resolves to on disk
207
+ def resolve(name)
208
+ @include_paths.each do |dir|
209
+ path = ::File.join(dir, name)
210
+ return path if ::File.file?(path)
211
+ end
212
+ nil
213
+ end
214
+
215
+ # @param name [String] a virtual file name
216
+ # @return [String, nil] where it is on disk, spelled from the include path as it
217
+ # was given, as protoc prints it in diagnostics; nil when not under one
218
+ def disk_path(name)
219
+ @given_paths.each_with_index do |given, i|
220
+ next unless ::File.file?(::File.join(@include_paths[i], name))
221
+ return given.empty? ? name : "#{given}/#{name}"
222
+ end
223
+ nil
224
+ end
225
+
226
+ # protoc's CanonicalizePath: "." components, empty components and a
227
+ # trailing slash dropped
228
+ # @api private
229
+ def self.canonical_path(path)
230
+ parts = path.split("/").reject { |p| p.empty? || p == "." }
231
+ path.start_with?("/") ? "/#{parts.join("/")}" : parts.join("/")
232
+ end
233
+
234
+ # The options message protoc compiles into itself, from the bundled
235
+ # descriptor.proto, for files that do not import descriptor.proto
236
+ # @api private
237
+ def builtin_options_type(name)
238
+ @builtin ||= begin
239
+ builtin = Compiler.new(include_paths: [], pool: Schema::Pool.new)
240
+ builtin.compile(["google/protobuf/descriptor.proto"]).last
241
+ end
242
+ @builtin.find_message(name)
243
+ end
244
+
245
+ # The source-retention-stripped copy of a pool file, linked against the
246
+ # stripped copies of its imports
247
+ def output_file(file)
248
+ @output ||= Schema::Pool.new
249
+ existing = @output.file(file.name)
250
+ return existing if existing
251
+ file.dependency_files.each { |dep| output_file(dep) }
252
+ options_type = ->(name) { @pool.find_message(name) || builtin_options_type(name) }
253
+ @output.add(Retention.strip(file, @output, options_type), warn: false)
254
+ end
255
+ private :output_file
256
+
257
+ # protoc's SimpleDtoa: the shortest of %.15g and %.17g that reads back exactly
258
+ # @api private
259
+ def self.simple_dtoa(value)
260
+ Schema::DefaultValue.simple_dtoa(value)
261
+ end
262
+
263
+ # protoc's SimpleFtoa over the float nearest to `value`
264
+ # @api private
265
+ def self.simple_ftoa(value)
266
+ return "inf" if value == Float::INFINITY
267
+ return "-inf" if value == -Float::INFINITY
268
+ return "nan" if value.nan?
269
+ single = [value].pack("e").unpack1("e")
270
+ text = format("%.6g", single)
271
+ parsed = Float(text)
272
+ unless parsed.finite? && [parsed].pack("e").unpack1("e") == single && strtof_in_range?(parsed)
273
+ text = format("%.9g", single)
274
+ end
275
+ text
276
+ end
277
+
278
+ # strtof reports ERANGE for values that overflow or flush to zero
279
+ def self.strtof_in_range?(value)
280
+ return true if value == 0.0
281
+ single = [value].pack("e").unpack1("e")
282
+ single.finite? && single.abs >= 1.1754943508222875e-38
283
+ end
284
+ private_class_method :strtof_in_range?
285
+
286
+ C_ESCAPES = {10 => "\\n", 13 => "\\r", 9 => "\\t", 34 => "\\\"", 39 => "\\'", 92 => "\\\\"}.freeze
287
+
288
+ # absl::CEscape: protoc's spelling of bytes defaults
289
+ # @api private
290
+ def self.c_escape(bytes)
291
+ out = +""
292
+ bytes.each_byte do |b|
293
+ out << if (escaped = C_ESCAPES[b])
294
+ escaped
295
+ elsif b < 32 || b >= 127
296
+ format("\\%03o", b)
297
+ else
298
+ b
299
+ end
300
+ end
301
+ out
302
+ end
303
+ end
304
+ end