11# frozen_string_literal: true
22
3+ # rubocop:disable Metrics/BlockLength, Lint/MissingCopEnableDirective, Style/DocumentationMethod
4+
35require 'bundler/gem_tasks'
46require 'rb_sys/cargo/metadata'
57require 'rb_sys/extensiontask'
@@ -34,57 +36,244 @@ require 'yard'
3436YARD ::Rake ::YardocTask . new
3537
3638require 'fileutils'
39+ require 'google/protobuf'
3740
3841namespace :proto do
3942 desc 'Generate API and Core protobufs'
4043 task :generate do
4144 # Remove all existing
4245 FileUtils . rm_rf ( 'lib/temporalio/api' )
4346
44- # Collect set of API protos with Google ones removed
45- api_protos = Dir . glob ( 'ext/sdk-core/sdk-core-protos/protos/api_upstream/**/*.proto' ) . reject do |proto |
46- proto . include? ( 'google' )
47- end
47+ def generate_protos ( api_protos )
48+ # Generate API to temp dir and move
49+ FileUtils . rm_rf ( 'tmp-proto' )
50+ FileUtils . mkdir_p ( 'tmp-proto' )
51+ sh 'bundle exec grpc_tools_ruby_protoc ' \
52+ '--proto_path=ext/sdk-core/sdk-core-protos/protos/api_upstream ' \
53+ '--proto_path=ext/sdk-core/sdk-core-protos/protos/api_cloud_upstream ' \
54+ '--proto_path=ext/additional_protos ' \
55+ '--ruby_out=tmp-proto ' \
56+ "#{ api_protos . join ( ' ' ) } "
57+
58+ # Walk all generated Ruby files and cleanup content and filename
59+ Dir . glob ( 'tmp-proto/temporal/api/**/*.rb' ) do |path |
60+ # Fix up the import
61+ content = File . read ( path )
62+ content . gsub! ( %r{^require 'temporal/(.*)_pb'$} , "require 'temporalio/\\ 1'" )
63+ File . write ( path , content )
64+
65+ # Remove _pb from the filename
66+ FileUtils . mv ( path , path . sub ( '_pb' , '' ) )
67+ end
4868
49- # Generate API to temp dir and move
50- FileUtils . rm_rf ( 'tmp-proto' )
51- FileUtils . mkdir_p ( 'tmp-proto' )
52- sh 'bundle exec grpc_tools_ruby_protoc ' \
53- '--proto_path=ext/sdk-core/sdk-core-protos/protos/api_upstream ' \
54- '--ruby_out=tmp-proto ' \
55- "#{ api_protos . join ( ' ' ) } "
56-
57- # Walk all generated Ruby files and cleanup content and filename
58- Dir . glob ( 'tmp-proto/temporal/api/**/*.rb' ) do |path |
59- # Fix up the import
60- content = File . read ( path )
61- content . gsub! ( %r{^require 'temporal/(.*)_pb'$} , "require 'temporalio/\\ 1'" )
62- File . write ( path , content )
63-
64- # Remove _pb from the filename
65- FileUtils . mv ( path , path . sub ( '_pb' , '' ) )
69+ # Move from temp dir and remove temp dir
70+ FileUtils . cp_r ( 'tmp-proto/temporal/api' , 'lib/temporalio' )
71+ FileUtils . rm_rf ( 'tmp-proto' )
6672 end
6773
68- # Move from temp dir and remove temp dir
69- FileUtils . mv ( 'tmp-proto/temporal/api' , 'lib/temporalio' )
70- FileUtils . rm_rf ( 'tmp-proto' )
74+ # Generate from API with Google ones removed
75+ generate_protos ( Dir . glob ( 'ext/sdk-core/sdk-core-protos/protos/api_upstream/**/*.proto' ) . reject do |proto |
76+ proto . include? ( 'google' )
77+ end )
78+
79+ # Generate from Cloud API
80+ generate_protos ( Dir . glob ( 'ext/sdk-core/sdk-core-protos/protos/api_cloud_upstream/**/*.proto' ) )
81+
82+ # Generate additional protos
83+ generate_protos ( Dir . glob ( 'ext/additional_protos/**/*.proto' ) )
7184
7285 # Write files that will help with imports. We are requiring the
7386 # request_response and not the service because the service depends on Google
7487 # API annotations we don't want to have to depend on.
75- string_lit = "# frozen_string_literal: true\n \n "
88+ File . write (
89+ 'lib/temporalio/api/cloud/cloudservice.rb' ,
90+ <<~TEXT
91+ # frozen_string_literal: true
92+
93+ require 'temporalio/api/cloud/cloudservice/v1/request_response'
94+ TEXT
95+ )
7696 File . write (
7797 'lib/temporalio/api/workflowservice.rb' ,
78- "#{ string_lit } require 'temporalio/api/workflowservice/v1/request_response'\n "
98+ <<~TEXT
99+ # frozen_string_literal: true
100+
101+ require 'temporalio/api/workflowservice/v1/request_response'
102+ TEXT
79103 )
80104 File . write (
81105 'lib/temporalio/api/operatorservice.rb' ,
82- "#{ string_lit } require 'temporalio/api/operatorservice/v1/request_response'\n "
106+ <<~TEXT
107+ # frozen_string_literal: true
108+
109+ require 'temporalio/api/operatorservice/v1/request_response'
110+ TEXT
83111 )
84112 File . write (
85113 'lib/temporalio/api.rb' ,
86- "#{ string_lit } require 'temporalio/api/operatorservice'\n require 'temporalio/api/workflowservice'\n "
114+ <<~TEXT
115+ # frozen_string_literal: true
116+
117+ require 'temporalio/api/cloud/cloudservice'
118+ require 'temporalio/api/common/v1/grpc_status'
119+ require 'temporalio/api/errordetails/v1/message'
120+ require 'temporalio/api/operatorservice'
121+ require 'temporalio/api/workflowservice'
122+
123+ module Temporalio
124+ # Raw protocol buffer models.
125+ module Api
126+ end
127+ end
128+ TEXT
87129 )
130+
131+ # Write the service classes that have the RPC calls
132+ def write_service_file ( qualified_service_name :, file_name :, class_name :, service_enum :)
133+ # Do service lookup
134+ desc = Google ::Protobuf ::DescriptorPool . generated_pool . lookup ( qualified_service_name )
135+ raise 'Failed finding service descriptor' unless desc
136+
137+ # Open file to generate
138+ File . open ( "lib/temporalio/client/connection/#{ file_name } .rb" , 'w' ) do |file |
139+ file . puts <<~TEXT
140+ # frozen_string_literal: true
141+
142+ # Generated code. DO NOT EDIT!
143+
144+ require 'temporalio/api'
145+ require 'temporalio/client/connection/service'
146+ require 'temporalio/internal/bridge/client'
147+
148+ module Temporalio
149+ class Client
150+ class Connection
151+ # #{ class_name } API.
152+ class #{ class_name } < Service
153+ # @!visibility private
154+ def initialize(connection)
155+ super(connection, Internal::Bridge::Client::#{ service_enum } )
156+ end
157+ TEXT
158+
159+ desc . each do |method |
160+ # Camel case to snake case
161+ rpc = method . name . gsub ( /([A-Z])/ , '_\1' ) . downcase . delete_prefix ( '_' )
162+ file . puts <<-TEXT
163+
164+ # Calls #{ class_name } .#{ method . name } API call.
165+ #
166+ # @param request [#{ method . input_type . msgclass } ] API request.
167+ # @param rpc_retry [Boolean] Whether to implicitly retry known retryable errors.
168+ # @param rpc_metadata [Hash<String, String>, nil] Headers to include on the RPC call.
169+ # @param rpc_timeout [Float, nil] Number of seconds before timeout.
170+ # @return [#{ method . output_type . msgclass } ] API response.
171+ def #{ rpc } (request, rpc_retry: false, rpc_metadata: nil, rpc_timeout: nil)
172+ invoke_rpc(
173+ rpc: '#{ rpc } ',
174+ request_class: #{ method . input_type . msgclass } ,
175+ response_class: #{ method . output_type . msgclass } ,
176+ request:,
177+ rpc_retry:,
178+ rpc_metadata:,
179+ rpc_timeout:
180+ )
181+ end
182+ TEXT
183+ end
184+
185+ file . puts <<~TEXT
186+ end
187+ end
188+ end
189+ end
190+ TEXT
191+ end
192+ end
193+
194+ require './lib/temporalio/api/workflowservice/v1/service'
195+ write_service_file (
196+ qualified_service_name : 'temporal.api.workflowservice.v1.WorkflowService' ,
197+ file_name : 'workflow_service' ,
198+ class_name : 'WorkflowService' ,
199+ service_enum : 'SERVICE_WORKFLOW'
200+ )
201+ require './lib/temporalio/api/operatorservice/v1/service'
202+ write_service_file (
203+ qualified_service_name : 'temporal.api.operatorservice.v1.OperatorService' ,
204+ file_name : 'operator_service' ,
205+ class_name : 'OperatorService' ,
206+ service_enum : 'SERVICE_OPERATOR'
207+ )
208+ require './lib/temporalio/api/cloud/cloudservice/v1/service'
209+ write_service_file (
210+ qualified_service_name : 'temporal.api.cloud.cloudservice.v1.CloudService' ,
211+ file_name : 'cloud_service' ,
212+ class_name : 'CloudService' ,
213+ service_enum : 'SERVICE_CLOUD'
214+ )
215+
216+ # Generate Rust code
217+ def generate_rust_match_arm ( file :, qualified_service_name :, service_enum :, trait :)
218+ # Do service lookup
219+ desc = Google ::Protobuf ::DescriptorPool . generated_pool . lookup ( qualified_service_name )
220+ file . puts <<~TEXT
221+ #{ service_enum } => match call.rpc.as_str() {
222+ TEXT
223+
224+ # desc.sort_by { |a, b| a.name <=> b.name }.each do |method|
225+ desc . to_a . sort_by ( &:name ) . each do |method |
226+ # Camel case to snake case
227+ rpc = method . name . gsub ( /([A-Z])/ , '_\1' ) . downcase . delete_prefix ( '_' )
228+ file . puts <<~TEXT
229+ "#{ rpc } " => rpc_call!(self, block, call, #{ trait } , #{ rpc } ),
230+ TEXT
231+ end
232+ file . puts <<~TEXT
233+ _ => Err(error!("Unknown RPC call {}", call.rpc)),
234+ },
235+ TEXT
236+ end
237+ File . open ( 'ext/src/client_rpc_generated.rs' , 'w' ) do |file |
238+ file . puts <<~TEXT
239+ // Generated code. DO NOT EDIT!
240+
241+ use magnus::{block::Proc, value::Opaque, Error, Ruby};
242+ use temporal_client::{CloudService, OperatorService, WorkflowService};
243+
244+ use super::{error, rpc_call};
245+ use crate::client::{Client, RpcCall, SERVICE_CLOUD, SERVICE_OPERATOR, SERVICE_WORKFLOW};
246+
247+ impl Client {
248+ pub fn invoke_rpc(&self, service: u8, block: Opaque<Proc>, call: RpcCall) -> Result<(), Error> {
249+ match service {
250+ TEXT
251+ generate_rust_match_arm (
252+ file :,
253+ qualified_service_name : 'temporal.api.workflowservice.v1.WorkflowService' ,
254+ service_enum : 'SERVICE_WORKFLOW' ,
255+ trait : 'WorkflowService'
256+ )
257+ generate_rust_match_arm (
258+ file :,
259+ qualified_service_name : 'temporal.api.operatorservice.v1.OperatorService' ,
260+ service_enum : 'SERVICE_OPERATOR' ,
261+ trait : 'OperatorService'
262+ )
263+ generate_rust_match_arm (
264+ file :,
265+ qualified_service_name : 'temporal.api.cloud.cloudservice.v1.CloudService' ,
266+ service_enum : 'SERVICE_CLOUD' ,
267+ trait : 'CloudService'
268+ )
269+ file . puts <<~TEXT
270+ _ => Err(error!("Unknown service")),
271+ }
272+ }
273+ }
274+ TEXT
275+ end
276+ sh 'cargo' , 'fmt' , '--' , 'ext/src/client_rpc_generated.rs'
88277 end
89278end
90279
@@ -104,4 +293,5 @@ Rake::Task[:build].enhance([:copy_parent_files]) do
104293 rm [ 'LICENSE' , 'README.md' ]
105294end
106295
107- task default : [ :rubocop , 'rbs:install_collection' , :steep , :compile , :test ]
296+ # TODO(cretz): Add rbs:install_collection and :steep back when RBS is ready
297+ task default : %i[ rubocop compile test ]
0 commit comments