proto-convert 0.1.0

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 (3) hide show
  1. checksums.yaml +7 -0
  2. data/bin/proto-convert +193 -0
  3. metadata +81 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8dbe2b80d8695940e2103d5739243437284cf4f3ebebff33f101a0381a8ac364
4
+ data.tar.gz: de4f6515ec0ccc6249887969dd5e75f5045c93bf6ad4121426e2058aca91fc06
5
+ SHA512:
6
+ metadata.gz: 5ebe5839f579e94d492137a599060aa05607a9c8166b57126a78dbbd397cb460b260aa19ea0c02697cf8a1d2786377a606c5b76de01dfc81b0561c5bf73bb5ee
7
+ data.tar.gz: e6c4f8a87eff97289c0d4583544c6cbd57b616b9a548a4617e53a60b84394e42c3880ed34bdd68bffc65c8ca7484949224b83955c8b337af7214c2e5d227e816
@@ -0,0 +1,193 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # MIT License
5
+ #
6
+ # Copyright (c) 2020 Azeem Sajid
7
+ #
8
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ # of this software and associated documentation files (the "Software"), to deal
10
+ # in the Software without restriction, including without limitation the rights
11
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ # copies of the Software, and to permit persons to whom the Software is
13
+ # furnished to do so, subject to the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be included in all
16
+ # copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ # SOFTWARE.
25
+
26
+ # proto-convert
27
+ # A command-line tool to convert protobuf messages from binary to JSON and vice versa
28
+ # GitHub: https://github.com/iamAzeem/proto-convert
29
+ #
30
+ # Developed by Azeem Sajid <azeem.sajid@gmail.com>
31
+
32
+ require 'optparse'
33
+ require 'English'
34
+
35
+ VERSION = "#{$PROGRAM_NAME} 0.1.0"
36
+ AUTHOR_NAME = 'Azeem Sajid'
37
+ AUTHOR_EMAIL = 'azeem.sajid@gmail.com'
38
+ AUTHOR_INFO = "Developed by #{AUTHOR_NAME} <#{AUTHOR_EMAIL}>"
39
+ REPO_URL = 'https://github.com/iamAzeem/proto-convert'
40
+
41
+ def compile_proto(filename)
42
+ file_path = File.expand_path(filename)
43
+ file_dir = File.dirname(file_path)
44
+
45
+ `protoc --ruby_out.=#{file_dir} --proto_path=#{file_dir} #{file_path}`
46
+ raise StandardError, "Invalid schema! [#{filename}] Resolve error(s)." unless $CHILD_STATUS.success?
47
+
48
+ compiled_proto = file_dir + '/' + File.basename(file_path, '.proto') + '_pb.rb'
49
+ raise StandardError, "Compiled schema not found! [#{compiled_proto}]" unless File.file?(compiled_proto)
50
+
51
+ compiled_proto
52
+ end
53
+
54
+ def valid_msgtype?(compiled_proto, msg_type)
55
+ msg_types = []
56
+ File.foreach(compiled_proto) do |line|
57
+ if line.lstrip.start_with?('add_message')
58
+ extracted_msg_type = line[/"([^"]*)"/, 1].freeze # regex: <add_message> 'msg_type' <do>
59
+ msg_types.push(extracted_msg_type) unless extracted_msg_type.nil?
60
+ end
61
+ end
62
+
63
+ msg_types.include?(msg_type)
64
+ end
65
+
66
+ def msg_class(compiled_proto, msg_type)
67
+ require compiled_proto
68
+ msg = Google::Protobuf::DescriptorPool.generated_pool.lookup(msg_type)
69
+ msg.msgclass
70
+ end
71
+
72
+ def convert(compiled_proto, msg_type, conversion_mode, input_file, output_file)
73
+ pb_msg_class = msg_class(compiled_proto, msg_type)
74
+ raise StandardError, "Message type ['#{msg_type}'] not registered!'" if pb_msg_class.nil?
75
+
76
+ begin
77
+ case conversion_mode
78
+ when :binary2json
79
+ input_msg = File.open(input_file, 'rb').read
80
+ puts ">> [B] #{input_file} (#{input_msg.length} bytes)"
81
+
82
+ decoded_msg = pb_msg_class.decode(input_msg)
83
+ output_msg = pb_msg_class.encode_json(decoded_msg)
84
+
85
+ File.open(output_file, 'w').write(output_msg)
86
+ puts "<< [J] #{output_file} (#{output_msg.length} bytes)"
87
+ when :json2binary
88
+ input_msg = File.open(input_file, 'r').read
89
+ puts ">> [J] #{input_file} (#{input_msg.length} bytes)"
90
+
91
+ decoded_msg = pb_msg_class.decode_json(input_msg)
92
+ output_msg = pb_msg_class.encode(decoded_msg)
93
+
94
+ File.open(output_file, 'wb').write(output_msg)
95
+ puts "<< [B] #{output_file} (#{output_msg.length} bytes)"
96
+ end
97
+ rescue Google::Protobuf::ParseError
98
+ raise StandardError, "Incompatible input message! [msgtype: #{msg_type}] #{$ERROR_INFO}"
99
+ rescue StandardError
100
+ raise StandardError, "Conversion failed! #{$ERROR_INFO}"
101
+ end
102
+ end
103
+
104
+ def start
105
+ mandatory_args = %i[mode proto msgtype input output].freeze
106
+
107
+ options = {}
108
+ mandatory_args.each { |arg| options[arg] = nil }
109
+
110
+ parser = OptionParser.new do |opts|
111
+ opts.banner = "Usage: #{$PROGRAM_NAME} -m [mode] -p [proto] -t [msgtype] -i [input] -o [output]"
112
+ opts.separator ''
113
+
114
+ modes = %i[binary2json b2j json2binary j2b].freeze
115
+ opts.on('-m', '--mode [MODE]', modes, "conversion mode #{modes.map(&:to_s)}") do |mode|
116
+ raise OptionParser::InvalidArgument unless modes.include?(mode)
117
+
118
+ if mode == :b2j
119
+ mode = :binary2json
120
+ elsif mode == :j2b
121
+ mode = :json2binary
122
+ end
123
+
124
+ options[:mode] = mode
125
+ end
126
+
127
+ opts.on('-p', '--proto [FILENAME]', 'protobuf schema (.proto)') do |filename|
128
+ raise StandardError, "Protobuf schema not found! [#{filename}]" unless File.file?(filename)
129
+
130
+ options[:proto] = filename
131
+ end
132
+
133
+ opts.on('-t', '--msgtype [TYPE]', 'fully-qualified message type') do |msgtype|
134
+ options[:msgtype] = msgtype
135
+ end
136
+
137
+ opts.on('-i', '--input [FILENAME]', 'source file (JSON/binary)') do |filename|
138
+ raise StandardError, "Source/input file not found! [#{filename}]" unless File.file?(filename)
139
+
140
+ options[:input] = filename
141
+ end
142
+
143
+ opts.on('-o', '--output [FILENAME]', 'destination file (binary/JSON)') do |filename|
144
+ options[:output] = filename
145
+ end
146
+
147
+ opts.on('-v', '--version', 'prints version information') do
148
+ puts "#{VERSION}\n#{AUTHOR_INFO}"
149
+ puts "GitHub URL: #{REPO_URL}"
150
+ exit
151
+ end
152
+
153
+ opts.on('-h', '--help', 'prints help') do
154
+ puts "#{VERSION}\n\n#{opts}\n#{AUTHOR_INFO}"
155
+ puts "GitHub URL: #{REPO_URL}"
156
+ exit
157
+ end
158
+ end
159
+
160
+ begin
161
+ parser.parse!
162
+
163
+ # Validate missing mandatory arguments
164
+ missing_args = mandatory_args.select { |arg| options[arg].nil? }
165
+ raise OptionParser::MissingArgument, 'No arguments provided!' if missing_args.length == mandatory_args.length
166
+ raise OptionParser::MissingArgument, "--#{missing_args.join(', --')}" unless missing_args.empty?
167
+
168
+ # Compile and validate proto and msgtype
169
+ compiled_proto = compile_proto(options[:proto])
170
+ msg_type = options[:msgtype]
171
+ raise OptionParser::InvalidArgument, "--msgtype #{msg_type}" unless valid_msgtype?(compiled_proto, msg_type)
172
+
173
+ # Convert and write to output file
174
+ conversion_mode = options[:mode]
175
+ input_file = options[:input]
176
+ output_file = options[:output]
177
+ convert(compiled_proto, msg_type, conversion_mode, input_file, output_file)
178
+ rescue OptionParser::InvalidOption, OptionParser::InvalidArgument, OptionParser::MissingArgument
179
+ puts $ERROR_INFO
180
+ puts "\n#{VERSION}\n\n#{parser}\n#{AUTHOR_INFO}"
181
+ exit 1
182
+ rescue LoadError
183
+ puts "Possible 'import' issue! Use a single self-contianed .proto file! #{$ERROR_INFO}"
184
+ exit 1
185
+ rescue StandardError
186
+ puts "ERROR: #{$ERROR_INFO}"
187
+ exit 1
188
+ ensure
189
+ File.delete(compiled_proto) if !compiled_proto.nil? && File.file?(compiled_proto)
190
+ end
191
+ end
192
+
193
+ start
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: proto-convert
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Azeem Sajid
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-07-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.14'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.14'
27
+ - !ruby/object:Gem::Dependency
28
+ name: google-protobuf
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.12'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 3.12.2
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '3.12'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 3.12.2
47
+ description: A command line tool to convert protobuf messages from binary to JSON
48
+ and vice versa
49
+ email:
50
+ - azeem.sajid@gmail.com
51
+ executables:
52
+ - proto-convert
53
+ extensions: []
54
+ extra_rdoc_files: []
55
+ files:
56
+ - bin/proto-convert
57
+ homepage: https://github.com/iamAzeem/proto-convert
58
+ licenses:
59
+ - MIT
60
+ metadata: {}
61
+ post_install_message:
62
+ rdoc_options: []
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ requirements: []
76
+ rubyforge_project:
77
+ rubygems_version: 2.7.6
78
+ signing_key:
79
+ specification_version: 4
80
+ summary: Protobuf Message Converter [Binary <-> JSON]
81
+ test_files: []