haveapi-rust-client 0.32.1 → 0.32.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7e17ea59650beae1310a6aae92279beccfd6e201be3758781242b3432078734d
4
- data.tar.gz: e3c01f11fcc5c75163c23698c0c712cdd1c8ae326a94e9b424241acf098ab32f
3
+ metadata.gz: e3657951314ad02a5a534a9244f93c4bd923757d5180acf0d8b3d64729854dd0
4
+ data.tar.gz: 27a9d60a419ccd4cb82467881f58f5fdff057040a651d1986183acf4b3100862
5
5
  SHA512:
6
- metadata.gz: ed8c66f3d2c3ca2e463fd81f20035f0c02f62384ae4cd01a41a85524aa2ed8e13f086a149155de80f0c7346efe87d0b9b50bcc6d0edce1e975d42b8fac523fe1
7
- data.tar.gz: fd0d3e09eadbae9731eed7cafa4614a59fb517a1199de045ab994e461e4811fd0be8b2b9597f17e68facfe045358a762c865b74945a9cfbb188f7971dd8fae9f
6
+ metadata.gz: 26bd3353e67c9f073eb6adee0472446a035d592047c52b82a91f7782f5195e4236779329e38346cbafead10f4e6affd0128e3f990ddd1e907b3a47480d556abc
7
+ data.tar.gz: dfdf5909918ed92b0cda4fd5a63b8e30612402506f091d1d20cde5126b339eaa6f3f1d1e99888e34e946722b3294035be3e5018031b7cc0caf708ca0b0e9e126
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ../ruby
3
3
  specs:
4
- haveapi-client (0.32.1)
4
+ haveapi-client (0.32.2)
5
5
  activesupport (>= 7.0)
6
6
  highline (~> 3.1)
7
7
  json
@@ -12,9 +12,9 @@ PATH
12
12
  PATH
13
13
  remote: .
14
14
  specs:
15
- haveapi-rust-client (0.32.1)
15
+ haveapi-rust-client (0.32.2)
16
16
  fiddle (~> 1.1, >= 1.1.8)
17
- haveapi-client (~> 0.32.1)
17
+ haveapi-client (~> 0.32.2)
18
18
  tomlrb (~> 2.0)
19
19
 
20
20
  GEM
@@ -39,7 +39,7 @@ GEM
39
39
  concurrent-ruby (1.3.8)
40
40
  connection_pool (3.0.2)
41
41
  diff-lcs (1.6.2)
42
- domain_name (0.7.20261007)
42
+ domain_name (0.7.20261031)
43
43
  drb (2.2.3)
44
44
  fiddle (1.1.8)
45
45
  hana (1.3.7)
@@ -152,12 +152,12 @@ CHECKSUMS
152
152
  concurrent-ruby (1.3.8) sha256=b2f1be836e968ccc78ccfce277ea79c72a88633f22306782c16ff23fb415d1e1
153
153
  connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a
154
154
  diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
155
- domain_name (0.7.20261007) sha256=0976c6ab837dbde991b433d03acc38fdb91fa8839df31a1ed75ce0318dc0441c
155
+ domain_name (0.7.20261031) sha256=6091f1c2b4781302de49eefef9ebcfbd758b84279898ea8918d0a59fdc5c0cc9
156
156
  drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373
157
157
  fiddle (1.1.8) sha256=7fa8ee3627271497f3add5503acdbc3f40b32f610fc1cf49634f083ef3f32eee
158
158
  hana (1.3.7) sha256=5425db42d651fea08859811c29d20446f16af196308162894db208cac5ce9b0d
159
- haveapi-client (0.32.1)
160
- haveapi-rust-client (0.32.1)
159
+ haveapi-client (0.32.2)
160
+ haveapi-rust-client (0.32.2)
161
161
  highline (3.1.2) sha256=67cbd34d19f6ef11a7ee1d82ffab5d36dfd5b3be861f450fc1716c7125f4bb4a
162
162
  http-accept (1.7.0) sha256=c626860682bfbb3b46462f8c39cd470fd7b0584f61b3cc9df5b2e9eb9972a126
163
163
  http-cookie (1.1.6) sha256=ba4b82be64de61dc281243dac70e3c382c45142f20268ed9276a3670c93feaa9
@@ -77,7 +77,7 @@ fields and samples, including across users and locales. For saved models, each
77
77
  sample must match its route, method, layouts and supplied parameter contracts.
78
78
  Unavailable or stale samples produce a limitation. Private schema, hidden
79
79
  defaults, local paths and initialization diagnostics never enter rendered output.
80
- See the [server profile guide](../../../doc/client-examples.md) for compatibility
80
+ See the [server profile guide](../../../doc/en/client-examples.md) for compatibility
81
81
  and rollback requirements.
82
82
 
83
83
  The adapter selects the action or advertised authentication provider from the
@@ -148,7 +148,7 @@ nix develop .#server-ruby --command bash -c \
148
148
 
149
149
  The performance unit specs count normalization work around real renders and
150
150
  check output/authorization parity as unrelated resource trees grow. They use no
151
- wall-clock budget. The opt-in [documentation benchmark](../../../doc/client-examples.md#documentation-benchmark)
151
+ wall-clock budget. The opt-in [documentation benchmark](../../../doc/en/client-examples.md#documentation-benchmark)
152
152
  adds timing and allocation evidence. With server and Rust generator Ruby
153
153
  dependencies prepared, pass `--rust` through `BENCHMARK_DOCUMENTATION_ARGS` to
154
154
  include a matching profile in HTML and measure async/blocking adapter action and
@@ -9,7 +9,16 @@ module HaveAPI::RustClient
9
9
  SIMPLE_DETAIL_LEVELS = [:simple].freeze
10
10
  DETAIL_LEVELS = %i[simple thorough].freeze
11
11
 
12
- class Unsupported < StandardError; end
12
+ class Unsupported < StandardError
13
+ attr_reader :key, :default, :values
14
+
15
+ def initialize(key, default:, **values)
16
+ @key = key
17
+ @default = default
18
+ @values = values
19
+ super(values.empty? ? default : format(default, values))
20
+ end
21
+ end
13
22
 
14
23
  def self.from_description(description:, api_version:, protocol_version:, mount_path:,
15
24
  crate_name:, crate_version: nil, source: nil)
@@ -76,18 +85,18 @@ module HaveAPI::RustClient
76
85
  end
77
86
 
78
87
  def render(kind:, context:, mode:, auth_method: nil, auth_description: nil)
79
- unsupported!('Unsupported Rust execution mode.') unless %i[async blocking].include?(mode)
80
- unsupported!('The Rust API client targets a different API version.') unless context.version.to_s == @snapshot['api_version']
88
+ unsupported!('generated.rust.mode', default: 'Unsupported Rust execution mode.') unless %i[async blocking].include?(mode)
89
+ unsupported!('generated.rust.version', default: 'The Rust API client targets a different API version.') unless context.version.to_s == @snapshot['api_version']
81
90
 
82
91
  source = case kind
83
92
  when :init then program(context, mode, [])
84
93
  when :auth then authentication(context, mode, auth_method, auth_description)
85
94
  when :example then example(context, mode)
86
- else unsupported!('Unsupported Rust documentation section.')
95
+ else unsupported!('generated.rust.section', default: 'Unsupported Rust documentation section.')
87
96
  end
88
97
  { status: :code, code: source.freeze }.freeze
89
98
  rescue Unsupported => e
90
- { status: :limitation, reason: e.message.freeze }.freeze
99
+ { status: :limitation, reason: documentation_message(e.key, context:, default: e.default, **e.values).freeze }.freeze
91
100
  end
92
101
 
93
102
  def detail_levels_for(kind:, context:, result:, mode:, auth_method: nil, auth_description: nil)
@@ -96,12 +105,32 @@ module HaveAPI::RustClient
96
105
 
97
106
  private
98
107
 
108
+ def documentation_message(key, context:, default:, **values)
109
+ if context.respond_to?(:documentation_message)
110
+ context.documentation_message(key, default:, **values)
111
+ else
112
+ values.empty? ? default : format(default, values)
113
+ end
114
+ end
115
+
116
+ def example_comment(key, context:, default:, **values)
117
+ if context.respond_to?(:example_comment)
118
+ context.example_comment(key, default:, **values)
119
+ else
120
+ values.empty? ? default : format(default, values)
121
+ end
122
+ end
123
+
124
+ def comment_lines(text, prefix)
125
+ text.gsub(/\r\n?/, "\n").split("\n", -1).map { |line| "#{prefix}#{line}" }.join("\n")
126
+ end
127
+
99
128
  def failed_sample?(sample)
100
129
  sample['status'] == false || (sample['http_status'].is_a?(Integer) && sample['http_status'] >= 400)
101
130
  end
102
131
 
103
- def unsupported!(reason)
104
- raise Unsupported, reason
132
+ def unsupported!(key, default:, **values)
133
+ raise Unsupported.new(key, default:, **values)
105
134
  end
106
135
 
107
136
  def freeze_data(value, seen = {}.compare_by_identity)
@@ -136,7 +165,7 @@ module HaveAPI::RustClient
136
165
  dependencies = dependency_lines(mode)
137
166
  dependencies << '// cargo add tokio --features macros,rt-multi-thread' if mode == :async
138
167
  dependencies << '// cargo add serde_json'
139
- dependencies << '// Construct, use and drop this blocking client outside async runtime threads.' if mode == :blocking
168
+ dependencies << comment_lines(example_comment('rust_blocking_context', default: 'Construct, use and drop this blocking client outside async runtime threads.', context:), '// ') if mode == :blocking
140
169
  client = mode == :blocking ? "#{@import}::blocking::Client" : "#{@import}::Client"
141
170
  lines = dependencies + ["use #{client};", '']
142
171
  lines << '#[tokio::main]' if mode == :async
@@ -173,23 +202,23 @@ module HaveAPI::RustClient
173
202
  live = normalized_members(providers)[provider]
174
203
  saved = @snapshot.fetch('description').fetch('authentication')[provider]
175
204
  unless live.is_a?(Hash) && saved
176
- unsupported!('This authentication provider is unavailable in the authorized Rust API client profile.')
205
+ unsupported!('generated.rust.authentication', default: 'This authentication provider is unavailable in the authorized Rust API client profile.')
177
206
  end
178
207
  live = normalize(live)
179
- unsupported!('The authentication description differs from the authorized help.') unless normalize(description) == live
208
+ unsupported!('generated.rust.authentication_description', default: 'The authentication description differs from the authorized help.') unless normalize(description) == live
180
209
 
181
210
  credentials = case provider
182
211
  when 'basic'
183
212
  ['.basic_auth("user", "secret")']
184
213
  when 'token', 'oauth2'
185
214
  unless saved['http_header'] == live['http_header']
186
- unsupported!('The Rust API client authentication header differs from this API.')
215
+ unsupported!('generated.rust.authentication_header', default: 'The Rust API client authentication header differs from this API.')
187
216
  end
188
217
  setter = provider == 'token' ? 'token' : 'oauth2_token'
189
218
  [".#{setter}(#{literal(provider == 'token' ? 'example-token' : 'example-access-token')})"]
190
- else unsupported!('This authentication provider is unsupported by the Rust client.')
219
+ else unsupported!('generated.rust.authentication_unsupported', default: 'This authentication provider is unsupported by the Rust client.')
191
220
  end
192
- body = ['// Configure existing credentials only; building does not validate them with the server.',
221
+ body = [comment_lines(example_comment('rust_credentials', default: 'Configure existing credentials only; building does not validate them with the server.', context:), '// '),
193
222
  'let _ = &api;']
194
223
  program(context, mode, body, credentials:)
195
224
  end
@@ -197,16 +226,16 @@ module HaveAPI::RustClient
197
226
  def example(context, mode)
198
227
  saved = resolve(resource_path: context.resource_path, action_name: context.action_name)
199
228
  live = authorized_action(context)
200
- unsupported!('This action is unavailable in the authorized Rust API client profile.') unless saved && live
229
+ unsupported!('generated.rust.action', default: 'This action is unavailable in the authorized Rust API client profile.') unless saved && live
201
230
  sample = normalize(context.sample)
202
231
  unless sample.is_a?(Hash) && (%w[path_params request response errors].any? { |key| !sample[key].nil? } ||
203
232
  sample['status'] == false || (sample['http_status'] && sample['http_status'] != 200))
204
- unsupported!('This sample has no structured request or response to render.')
233
+ unsupported!('generated.rust.structured_sample', default: 'This sample has no structured request or response to render.')
205
234
  end
206
235
  compatible_action!(saved, live, context)
207
236
  paths = sample['path_params'] || []
208
237
  unless paths.is_a?(Array) && paths.length == saved[:placeholders].length && paths.all? { |value| valid_path?(value) }
209
- unsupported!('The sample path arguments cannot be represented by this Rust API client.')
238
+ unsupported!('generated.rust.path_parameters', default: 'The sample path arguments cannot be represented by this Rust API client.')
210
239
  end
211
240
  inputs = input_lines(saved, live, sample['request'])
212
241
  validate_response!(saved, live, sample['response']) unless sample['response'].nil?
@@ -221,7 +250,7 @@ module HaveAPI::RustClient
221
250
  ' Err(error) => {', ' eprintln!("HTTP status: {:?}", error.http_status());']
222
251
  if context.detail == :thorough
223
252
  body += [' eprintln!("Request failed: {}; timeout: {}", error, error.is_timeout());',
224
- ' // Inspect these explicitly; server diagnostics can contain sensitive values.',
253
+ comment_lines(example_comment('rust_diagnostics', default: 'Inspect these explicitly; server diagnostics can contain sensitive values.', context:), ' // '),
225
254
  ' let _ = (error.server_message(), error.validation_errors());']
226
255
  end
227
256
  body += [' }', '}']
@@ -231,16 +260,20 @@ module HaveAPI::RustClient
231
260
  body.concat(output_lines(saved, sample['response']))
232
261
  end
233
262
  if saved[:desc]['blocking'] == true
234
- body << '// Request success or a timeout does not establish completion of a server-side operation.'
263
+ body << comment_lines(example_comment('rust_completion', default: 'Request success or a timeout does not establish completion of a server-side operation.', context:), '// ')
235
264
  if context.detail == :thorough
236
- body << '// Use only the separately documented action-state action; this API client has no automatic wait helper.'
265
+ body << comment_lines(example_comment('rust_action_state', default: 'Use only the separately documented action-state action; this API client has no automatic wait helper.', context:), '// ')
237
266
  end
238
267
  end
239
268
  credentials = action_credentials(saved, context)
240
269
  return program(context, mode, body, credentials:) if context.detail == :thorough
241
270
 
242
- enclosing = mode == :async ? 'an async function' : 'a synchronous function outside async runtime threads'
243
- (["// Use inside #{enclosing} returning Result<(), Box<dyn std::error::Error>>."] + body + ['']).join("\n")
271
+ enclosing = if mode == :async
272
+ comment_lines(example_comment('rust_async_function', default: 'Use inside an async function returning %{type}.', context:, type: 'Result<(), Box<dyn std::error::Error>>'), '// ')
273
+ else
274
+ comment_lines(example_comment('rust_sync_function', default: 'Use inside a synchronous function outside async runtime threads returning %{type}.', context:, type: 'Result<(), Box<dyn std::error::Error>>'), '// ')
275
+ end
276
+ ([enclosing] + body + ['']).join("\n")
244
277
  end
245
278
 
246
279
  def action_credentials(action, context)
@@ -260,7 +293,7 @@ module HaveAPI::RustClient
260
293
  setter = provider == 'token' ? 'token' : 'oauth2_token'
261
294
  return [".#{setter}(#{literal(provider == 'token' ? 'example-token' : 'example-access-token')})"]
262
295
  end
263
- unsupported!('This action requires authentication unavailable in the authorized Rust API client profile.')
296
+ unsupported!('generated.rust.action_authentication', default: 'This action requires authentication unavailable in the authorized Rust API client profile.')
264
297
  end
265
298
 
266
299
  def authorized_action(context)
@@ -282,7 +315,7 @@ module HaveAPI::RustClient
282
315
  mount += '/' unless mount.end_with?('/')
283
316
  unless live['method'] == saved[:desc]['method'] && live['path'] == mount + saved[:route] &&
284
317
  (live['auth'] == false) == (saved[:desc]['auth'] == false)
285
- unsupported!('The Rust API client route or HTTP method differs from this API action.')
318
+ unsupported!('generated.rust.route', default: 'The Rust API client route or HTTP method differs from this API action.')
286
319
  end
287
320
  %w[input output].each do |direction|
288
321
  saved_io = saved[direction.to_sym]
@@ -291,17 +324,17 @@ module HaveAPI::RustClient
291
324
 
292
325
  unless saved_io && live_io && saved_io[:desc]['layout'] == live_io['layout'] &&
293
326
  saved_io[:desc]['namespace'] == live_io['namespace'] && saved_io[:desc]['format'] == live_io['format']
294
- unsupported!('The Rust API client input or output layout differs from this API action.')
327
+ unsupported!('generated.rust.layout', default: 'The Rust API client input or output layout differs from this API action.')
295
328
  end
296
329
  end
297
330
  %i[global_input object_input].each do |key|
298
331
  next unless saved[key] && saved[key][:parameters].any? { |parameter| parameter[:desc]['required'] == true }
299
332
 
300
- unsupported!('This Rust API client action requires metadata that is absent from the sample.')
333
+ unsupported!('generated.rust.action_metadata', default: 'This Rust API client action requires metadata that is absent from the sample.')
301
334
  end
302
335
  %w[global object].each do |role|
303
336
  if live.dig('meta', role, 'input', 'parameters')&.any? { |_name, parameter| parameter['required'] == true }
304
- unsupported!('This action requires metadata that is absent from the sample.')
337
+ unsupported!('generated.rust.sample_metadata', default: 'This action requires metadata that is absent from the sample.')
305
338
  end
306
339
  end
307
340
  end
@@ -313,18 +346,18 @@ module HaveAPI::RustClient
313
346
  def input_lines(saved, live, request)
314
347
  io = saved[:input]
315
348
  if io.nil?
316
- unsupported!('This Rust API client action cannot represent the sample input.') unless request.nil? || request == {}
349
+ unsupported!('generated.rust.input', default: 'This Rust API client action cannot represent the sample input.') unless request.nil? || request == {}
317
350
  return { before: [], chain: '' }
318
351
  end
319
352
  list = io[:layout].end_with?('_list')
320
- unsupported!('Rust GET requests do not support list input.') if list && saved[:desc]['method'] == 'GET'
353
+ unsupported!('generated.rust.get_list', default: 'Rust GET requests do not support list input.') if list && saved[:desc]['method'] == 'GET'
321
354
  values = if request.nil?
322
355
  list ? [] : {}
323
356
  else
324
357
  request
325
358
  end
326
359
  unless list ? values.is_a?(Array) && values.all?(Hash) : values.is_a?(Hash)
327
- unsupported!('The sample input does not match the Rust API client layout.')
360
+ unsupported!('generated.rust.input_layout', default: 'The sample input does not match the Rust API client layout.')
328
361
  end
329
362
  objects = list ? values : [values]
330
363
  objects.each { |object| validate_object!(io, live['input'], object, required: true, method: saved[:desc]['method']) }
@@ -352,20 +385,20 @@ module HaveAPI::RustClient
352
385
  parameter = saved_io[:parameters].find { |item| item[:name] == name }
353
386
  live_parameter = live_params && live_params[name]
354
387
  unless parameter && live_parameter && compatible_parameter?(parameter[:desc], live_parameter)
355
- unsupported!('A sample parameter is unavailable or has changed in this Rust API client.')
388
+ unsupported!('generated.rust.parameter', default: 'A sample parameter is unavailable or has changed in this Rust API client.')
356
389
  end
357
390
  unless valid_value?(parameter[:desc], value, output: !required)
358
- unsupported!('A sample value cannot be represented by this Rust API client.')
391
+ unsupported!('generated.rust.value', default: 'A sample value cannot be represented by this Rust API client.')
359
392
  end
360
393
  if method == 'GET' && parameter[:desc]['type'] == 'Custom' && (value.is_a?(Hash) || value.is_a?(Array))
361
- unsupported!('Rust GET requests do not support nested Custom query values.')
394
+ unsupported!('generated.rust.get_custom', default: 'Rust GET requests do not support nested Custom query values.')
362
395
  end
363
396
  end
364
397
  return unless required
365
398
 
366
399
  [saved_io[:desc].fetch('parameters'), live_params || {}].each do |parameters|
367
400
  if parameters.any? { |name, desc| desc['required'] == true && !object.has_key?(name) }
368
- unsupported!('The sample omits a required Rust API client input.')
401
+ unsupported!('generated.rust.required_input', default: 'The sample omits a required Rust API client input.')
369
402
  end
370
403
  end
371
404
  end
@@ -425,12 +458,12 @@ module HaveAPI::RustClient
425
458
  def validate_response!(saved, live, response)
426
459
  io = saved[:output]
427
460
  unless io
428
- unsupported!('The sample response does not match this Rust API client.') unless response == {}
461
+ unsupported!('generated.rust.response', default: 'The sample response does not match this Rust API client.') unless response == {}
429
462
  return
430
463
  end
431
464
  list = io[:layout].end_with?('_list')
432
465
  unless list ? response.is_a?(Array) && response.all?(Hash) : response.is_a?(Hash)
433
- unsupported!('The sample response does not match this Rust API client layout.')
466
+ unsupported!('generated.rust.response_layout', default: 'The sample response does not match this Rust API client layout.')
434
467
  end
435
468
  (list ? response : [response]).each { |object| validate_object!(io, live['output'], object, required: false) }
436
469
  end
@@ -1,5 +1,5 @@
1
1
  module HaveAPI
2
2
  module RustClient
3
- VERSION = '0.32.1'.freeze
3
+ VERSION = '0.32.2'.freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: haveapi-rust-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.32.1
4
+ version: 0.32.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jakub Skokan
@@ -35,14 +35,14 @@ dependencies:
35
35
  requirements:
36
36
  - - "~>"
37
37
  - !ruby/object:Gem::Version
38
- version: 0.32.1
38
+ version: 0.32.2
39
39
  type: :runtime
40
40
  prerelease: false
41
41
  version_requirements: !ruby/object:Gem::Requirement
42
42
  requirements:
43
43
  - - "~>"
44
44
  - !ruby/object:Gem::Version
45
- version: 0.32.1
45
+ version: 0.32.2
46
46
  - !ruby/object:Gem::Dependency
47
47
  name: tomlrb
48
48
  requirement: !ruby/object:Gem::Requirement