haveapi-rust-client 0.31.2 → 0.32.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 98e92e7d6c1f69e9a4f37ffea0d246af9b242170f951b523c19db89fb0fc9bda
4
- data.tar.gz: 80614d87719c4bba512d3cf4c8b34f5f91c5db0517813c60784ab359feee3d42
3
+ metadata.gz: 680519d92eeac2f6ea9c1f6eb7c73fc03f197068a329c933db47cb9db42d6fc4
4
+ data.tar.gz: 05ac7f3e221583754c716d048f61ac585e89e47a0a1bd9b10ac9e8a533be6a95
5
5
  SHA512:
6
- metadata.gz: 0704356421435f6e4d5100de5904c8b0e2a4542a4e45cdfa8126b52bdb9b019e7ec1532441e5230f491a54ac29a0964ac100e4a4e952cdae42068eb6f9711f6c
7
- data.tar.gz: 6388b1c3f653abdf2fb1184127dd8e6e137a421d77fdf0aa3fa07d22fccc070af1107b479ce71c6b75c2e8c474c1d6b93d25de277b05c8becbfd6076099b7f69
6
+ metadata.gz: 7b0a52816bfa919ebfe50d35aad9c4405ddeaa145a93c326ba6f92daf7d31efb6c81c27550576e1b917463a9d5c33264cb0d7d6c33f1a6df1b1c9e117e74d7bd
7
+ data.tar.gz: a3f6d9e91b0c247e9930d614b95c53af5ef219aadd35cc09f2f2fe9f2b4a46f769883498a334f08ad5391d01e93c6d68eebe023128fd8212d8d35a4c614befb5
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ../ruby
3
3
  specs:
4
- haveapi-client (0.31.2)
4
+ haveapi-client (0.32.0)
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.31.2)
15
+ haveapi-rust-client (0.32.0)
16
16
  fiddle (~> 1.1, >= 1.1.8)
17
- haveapi-client (~> 0.31.2)
17
+ haveapi-client (~> 0.32.0)
18
18
  tomlrb (~> 2.0)
19
19
 
20
20
  GEM
@@ -50,7 +50,7 @@ GEM
50
50
  domain_name (~> 0.5)
51
51
  i18n (1.15.2)
52
52
  concurrent-ruby (~> 1.0)
53
- io-console (0.9.4)
53
+ io-console (0.9.5)
54
54
  json (2.21.2)
55
55
  json_schemer (2.5.0)
56
56
  bigdecimal
@@ -65,7 +65,7 @@ GEM
65
65
  mime-types (3.7.0)
66
66
  logger
67
67
  mime-types-data (~> 3.2025, >= 3.2025.0507)
68
- mime-types-data (3.2026.1020)
68
+ mime-types-data (3.2026.1027)
69
69
  minitest (6.0.6)
70
70
  drb (~> 2.0)
71
71
  prism (~> 1.5)
@@ -156,13 +156,13 @@ CHECKSUMS
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.31.2)
160
- haveapi-rust-client (0.31.2)
159
+ haveapi-client (0.32.0)
160
+ haveapi-rust-client (0.32.0)
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
164
164
  i18n (1.15.2) sha256=00f9eb62412fe593b2a65a97daa75300d37abb8f7202ec748e94b6d46a9dd1b5
165
- io-console (0.9.4) sha256=a403304f32928ee85e56d457b4612ca43f2703b561a3258844992bd24085fbb1
165
+ io-console (0.9.5) sha256=83dd9bbf4c4680416629245b39cd4212eb5f4d9a4ea91804870c1f6b846414d5
166
166
  json (2.21.2) sha256=1f1d3b7cf2b3ba1a69beca0bb6db13d5438b80bff3cd54cdaaa620b9b07c1c6a
167
167
  json_schemer (2.5.0) sha256=2f01fb4cce721a4e08dd068fc2030cffd0702a7f333f1ea2be6e8991f00ae396
168
168
  language_server-protocol (3.17.0.6) sha256=5ef2c0c138f8267e1bc631d3328347d354f96724b0af22f2c79516120443b7f0
@@ -170,7 +170,7 @@ CHECKSUMS
170
170
  logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
171
171
  mcp (0.25.0) sha256=8acf3a5f3b60b6fd6e5d9b8ac5eb85ac504766976cbadaca182d2ffe7d795d98
172
172
  mime-types (3.7.0) sha256=dcebf61c246f08e15a4de34e386ebe8233791e868564a470c3fe77c00eed5e56
173
- mime-types-data (3.2026.1020) sha256=6b3bcc776c1e3591267727700467e02fb9c12b503fcce4e0e964516d7c1e0d06
173
+ mime-types-data (3.2026.1027) sha256=4568729e44974c1c8a8ceb76f7a5fc164e8c9553335455688bb3c00020f5d83f
174
174
  minitest (6.0.6) sha256=153ea36d1d987a62942382b61075745042a2b3123b1cd48f4c3675af9cc7d6f1
175
175
  netrc (0.11.0) sha256=de1ce33da8c99ab1d97871726cba75151113f117146becbe45aa85cb3dabee3f
176
176
  parallel (1.28.0) sha256=33e6de1484baf2524792d178b0913fc8eb94c628d6cfe45599ad4458c638c970
data/docs/development.md CHANGED
@@ -6,7 +6,14 @@ From the HaveAPI root:
6
6
  nix develop .#client-rust
7
7
  cd clients/rust
8
8
  bundle exec rspec spec/unit
9
- bundle exec rubocop lib bin spec/unit spec/spec_helper.rb haveapi-rust-client.gemspec
9
+ ```
10
+
11
+ Run Ruby style checks from the HaveAPI root in the default Nix shell, using the
12
+ root bundle:
13
+
14
+ ```sh
15
+ BUNDLE_GEMFILE="$PWD/Gemfile" bundle exec rubocop clients/rust/lib clients/rust/bin \
16
+ clients/rust/spec clients/rust/haveapi-rust-client.gemspec
10
17
  ```
11
18
 
12
19
  The component shell runs `bundle install` for this library's dependency ranges
@@ -22,11 +29,21 @@ The root Git hooks also need the root shell's dependencies: prepare them with
22
29
  Overcommit hooks enabled. Do not install gems concurrently in the same gem home.
23
30
 
24
31
  From the HaveAPI root, `make test-rust-unit` runs the generator specs.
25
- `make test-rust` additionally runs both runtime feature configurations and the
26
- generated-client protocol harness. Unit specs never start a server or invoke
27
- Cargo. Compilation, Rust linting, server integration, MSRV and packaging checks
28
- can take longer; follow the development workspace's verification procedure when
29
- working there.
32
+ The specs check generated Rust with the recorded formatter, including failures
33
+ after a successful version probe, without starting Cargo or a server. They also
34
+ verify that every copied runtime file retains its maintained source bytes.
35
+ `make check-rust` runs the runtime's read-only `cargo fmt` check.
36
+ `make test-rust` additionally checks formatting and runs both runtime feature
37
+ configurations, the prepared server fixture contracts and the generated-client
38
+ protocol harness.
39
+ Quick unit specs need only the Rust component bundle; they never load the server
40
+ application, start a listener or invoke Cargo. Compilation, Rust linting, server
41
+ integration, MSRV and packaging checks can take longer; follow the development
42
+ workspace's verification procedure when working there.
43
+
44
+ The generated-client harness also runs `cargo fmt --check` over its complete
45
+ temporary crate, including the copied acceptance tests. This check precedes its
46
+ compilation and protocol tests. Clippy remains a separate runtime lint check.
30
47
 
31
48
  The Nix shell supplies the development compiler and formatter. New test
32
49
  fixtures record that formatter; existing clients retain their own exact
@@ -110,8 +110,15 @@ Their internal structural URL is not an SDK provenance source or emitted request
110
110
  URL. Requests use the real server context. Actual SDK generation still requires
111
111
  its full versioned packaging configuration; these factories do not relax it.
112
112
 
113
- Both variants show a complete program and Cargo dependency comments. Async
114
- programs use Tokio and `.send().await?`. Blocking programs enable the SDK's
113
+ Simple action examples assume that initialization and authentication have
114
+ established `api`. They show the actual call and sample output. Async fragments
115
+ belong in an async function returning `Result<(), Box<dyn std::error::Error>>`;
116
+ blocking fragments use a synchronous function with that return type, outside
117
+ async runtime threads. The `?` operator propagates request and conversion errors.
118
+ Both detail levels share sample validation and limitations.
119
+
120
+ **With error handling** shows a complete program and Cargo dependency comments.
121
+ Async programs use Tokio and `.send().await?`. Blocking programs enable the SDK's
115
122
  `blocking` feature and construct, use and drop the client in a synchronous
116
123
  `main`, outside async runtime threads. `HAVEAPI_BASE_URL` can override the shown
117
124
  mounted root. Authenticated actions use advertised existing credentials from
@@ -149,13 +156,15 @@ authentication rendering separately. These lanes do not generate or compile an
149
156
  SDK. Compare the same fixture, runtime, dependency inputs and output digests
150
157
  against both implementation revisions before claiming a measured improvement.
151
158
 
152
- The acceptance runner extracts actual `RustAsyncClient.render` and
153
- `RustBlockingClient.render` source. It generates a matching temporary fixture
159
+ The context must expose `detail` as `:simple` or `:thorough`. The acceptance runner
160
+ extracts actual `RustAsyncClient.render` and `RustBlockingClient.render` source.
161
+ It generates a matching temporary fixture
154
162
  SDK, compiles the emitted imports, paths, methods and types, and executes the
155
- programs against the existing disposable HaveAPI fixture at `/` and
156
- `/mounted/api/`. It adds output assertions without replacing emitted requests.
157
- Authentication checks compose the emitted authentication builder with the
158
- emitted action body. It tests both modes with the development compiler and
163
+ programs for both detail levels against the existing disposable HaveAPI fixture
164
+ at `/` and `/mounted/api/`. It adds output assertions without replacing emitted
165
+ requests. Simple fragments use separately rendered setup. Authentication checks
166
+ compose the emitted authentication builder with the emitted action body.
167
+ It tests both modes with the development compiler and
159
168
  Rust 1.95, including zero/false/empty/null/omitted inputs, nested Custom, lists,
160
169
  nested resources, failure branches, metadata, existing credentials and an
161
170
  action-state reference. The fixture uses local test credentials and in-memory
@@ -2,10 +2,13 @@ require 'haveapi/rust_client'
2
2
  require 'date'
3
3
 
4
4
  module HaveAPI::RustClient
5
- # Resolves the complete SDK model before checking a request's authorized help.
5
+ # Resolves the complete API client model before checking a request's authorized help.
6
6
  class Documentation
7
7
  include Model
8
8
 
9
+ SIMPLE_DETAIL_LEVELS = [:simple].freeze
10
+ DETAIL_LEVELS = %i[simple thorough].freeze
11
+
9
12
  class Unsupported < StandardError; end
10
13
 
11
14
  def self.from_description(description:, api_version:, protocol_version:, mount_path:,
@@ -24,29 +27,29 @@ module HaveAPI::RustClient
24
27
 
25
28
  def self.validate_source(source)
26
29
  return nil if source.nil?
27
- raise Error, 'invalid Rust SDK source' unless source.is_a?(Hash) && source.length == 2
30
+ raise Error, 'invalid Rust API client source' unless source.is_a?(Hash) && source.length == 2
28
31
 
29
32
  values = source.to_h { |key, value| [key.to_s, value] }
30
33
  url, revision = values.values_at('git', 'rev')
31
34
  unless values.keys.sort == %w[git rev] && url.is_a?(String) && !url.match?(/[\x00-\x20\x7f]/) &&
32
35
  revision.is_a?(String) && revision.match?(/\A[0-9a-fA-F]{40}\z/)
33
- raise Error, 'invalid Rust SDK source'
36
+ raise Error, 'invalid Rust API client source'
34
37
  end
35
38
 
36
39
  uri = URI.parse(url)
37
40
  unless uri.is_a?(URI::HTTPS) && uri.host && !uri.host.empty? &&
38
41
  uri.userinfo.nil? && uri.query.nil? && uri.fragment.nil?
39
- raise Error, 'invalid Rust SDK source'
42
+ raise Error, 'invalid Rust API client source'
40
43
  end
41
44
 
42
45
  values.transform_values { |value| value.dup.freeze }.freeze
43
46
  rescue URI::InvalidURIError
44
- raise Error, 'invalid Rust SDK source'
47
+ raise Error, 'invalid Rust API client source'
45
48
  end
46
49
 
47
50
  def initialize(snapshot:, config: nil, crate_name: nil, crate_version: nil, source: nil)
48
51
  if config
49
- raise Error, 'conflicting Rust SDK package settings' unless crate_name.nil? && crate_version.nil?
52
+ raise Error, 'conflicting Rust API client package settings' unless crate_name.nil? && crate_version.nil?
50
53
 
51
54
  config = JSON.parse(Snapshot.canonical(config))
52
55
  Generator.validate_config(config)
@@ -74,7 +77,7 @@ module HaveAPI::RustClient
74
77
 
75
78
  def render(kind:, context:, mode:, auth_method: nil, auth_description: nil)
76
79
  unsupported!('Unsupported Rust execution mode.') unless %i[async blocking].include?(mode)
77
- unsupported!('The Rust SDK targets a different API version.') unless context.version.to_s == @snapshot['api_version']
80
+ unsupported!('The Rust API client targets a different API version.') unless context.version.to_s == @snapshot['api_version']
78
81
 
79
82
  source = case kind
80
83
  when :init then program(context, mode, [])
@@ -87,8 +90,16 @@ module HaveAPI::RustClient
87
90
  { status: :limitation, reason: e.message.freeze }.freeze
88
91
  end
89
92
 
93
+ def detail_levels_for(kind:, context:, result:, mode:, auth_method: nil, auth_description: nil)
94
+ result[:status] == :code && %i[init auth example].include?(kind) && %i[async blocking].include?(mode) ? DETAIL_LEVELS : SIMPLE_DETAIL_LEVELS
95
+ end
96
+
90
97
  private
91
98
 
99
+ def failed_sample?(sample)
100
+ sample['status'] == false || (sample['http_status'].is_a?(Integer) && sample['http_status'] >= 400)
101
+ end
102
+
92
103
  def unsupported!(reason)
93
104
  raise Unsupported, reason
94
105
  end
@@ -130,14 +141,21 @@ module HaveAPI::RustClient
130
141
  lines = dependencies + ["use #{client};", '']
131
142
  lines << '#[tokio::main]' if mode == :async
132
143
  lines << "#{'async ' if mode == :async}fn main() -> Result<(), Box<dyn std::error::Error>> {"
133
- lines << " let base = std::env::var(\"HAVEAPI_BASE_URL\").unwrap_or_else(|_| #{literal(context.base_url)}.to_owned());"
134
- lines << ' let api = Client::builder(&base)'
135
- lines.concat(credentials.map { |line| " #{line}" })
136
- lines << ' .build()?;'
144
+ construction = "#{["Client::builder(#{literal(context.base_url)})", *credentials].join("\n ")}\n .build()"
145
+ lines.concat(result_lines(context, 'api', construction, 'Client configuration failed').flat_map { |line| line.lines(chomp: true).map { |part| " #{part}" } })
137
146
  lines.concat((body.empty? ? ['let _ = &api;'] : body).map { |line| line.empty? ? '' : " #{line}" })
138
147
  lines.push(' Ok(())', '}', '').join("\n")
139
148
  end
140
149
 
150
+ def result_lines(context, name, expression, label)
151
+ return ["let #{name} = #{expression}?;"] unless context.detail == :thorough
152
+
153
+ ["let #{name} = match #{expression} {", " Ok(#{name}) => #{name},", ' Err(error) => {',
154
+ " eprintln!(\"#{label}: {}\", error);",
155
+ ' eprintln!("HTTP status: {:?}; timeout: {}", error.http_status(), error.is_timeout());',
156
+ ' return Err(error.into());', ' }', '};']
157
+ end
158
+
141
159
  def dependency_lines(mode)
142
160
  unless @source || @crate_version
143
161
  return ["// cargo add #{@crate_name}#{' --features blocking' if mode == :blocking}"]
@@ -155,23 +173,23 @@ module HaveAPI::RustClient
155
173
  live = normalized_members(providers)[provider]
156
174
  saved = @snapshot.fetch('description').fetch('authentication')[provider]
157
175
  unless live.is_a?(Hash) && saved
158
- unsupported!('This authentication provider is unavailable in the authorized Rust SDK profile.')
176
+ unsupported!('This authentication provider is unavailable in the authorized Rust API client profile.')
159
177
  end
160
178
  live = normalize(live)
161
179
  unsupported!('The authentication description differs from the authorized help.') unless normalize(description) == live
162
180
 
163
181
  credentials = case provider
164
182
  when 'basic'
165
- ['.basic_auth(std::env::var("HAVEAPI_USER")?, std::env::var("HAVEAPI_PASSWORD")?)']
183
+ ['.basic_auth("user", "secret")']
166
184
  when 'token', 'oauth2'
167
185
  unless saved['http_header'] == live['http_header']
168
- unsupported!('The Rust SDK authentication header differs from this API.')
186
+ unsupported!('The Rust API client authentication header differs from this API.')
169
187
  end
170
188
  setter = provider == 'token' ? 'token' : 'oauth2_token'
171
- [".#{setter}(std::env::var(\"HAVEAPI_TOKEN\")?)"]
189
+ [".#{setter}(#{literal(provider == 'token' ? 'example-token' : 'example-access-token')})"]
172
190
  else unsupported!('This authentication provider is unsupported by the Rust client.')
173
191
  end
174
- body = ['// Existing credentials only. Token acquisition and challenges require explicit advertised actions.',
192
+ body = ['// Configure existing credentials only; building does not validate them with the server.',
175
193
  'let _ = &api;']
176
194
  program(context, mode, body, credentials:)
177
195
  end
@@ -179,7 +197,7 @@ module HaveAPI::RustClient
179
197
  def example(context, mode)
180
198
  saved = resolve(resource_path: context.resource_path, action_name: context.action_name)
181
199
  live = authorized_action(context)
182
- unsupported!('This action is unavailable in the authorized Rust SDK profile.') unless saved && live
200
+ unsupported!('This action is unavailable in the authorized Rust API client profile.') unless saved && live
183
201
  sample = normalize(context.sample)
184
202
  unless sample.is_a?(Hash) && (%w[path_params request response errors].any? { |key| !sample[key].nil? } ||
185
203
  sample['status'] == false || (sample['http_status'] && sample['http_status'] != 200))
@@ -188,7 +206,7 @@ module HaveAPI::RustClient
188
206
  compatible_action!(saved, live, context)
189
207
  paths = sample['path_params'] || []
190
208
  unless paths.is_a?(Array) && paths.length == saved[:placeholders].length && paths.all? { |value| valid_path?(value) }
191
- unsupported!('The sample path arguments cannot be represented by this Rust SDK.')
209
+ unsupported!('The sample path arguments cannot be represented by this Rust API client.')
192
210
  end
193
211
  inputs = input_lines(saved, live, sample['request'])
194
212
  validate_response!(saved, live, sample['response']) unless sample['response'].nil?
@@ -198,18 +216,31 @@ module HaveAPI::RustClient
198
216
  call << inputs.fetch(:chain)
199
217
  send = mode == :async ? '.send().await' : '.send()'
200
218
  body = inputs.fetch(:before)
201
- if sample['status'] == false || (sample['http_status'].is_a?(Integer) && sample['http_status'] >= 400)
219
+ if failed_sample?(sample)
202
220
  body += ["match #{call}#{send} {", ' Ok(_) => return Err("expected the documented request to fail".into()),',
203
- ' Err(error) => {', ' let _ = error.http_status();',
204
- ' // Inspect these explicitly; server diagnostics can contain sensitive values.',
205
- ' let _ = (error.server_message(), error.validation_errors());', ' }', '}']
221
+ ' Err(error) => {', ' eprintln!("HTTP status: {:?}", error.http_status());']
222
+ if context.detail == :thorough
223
+ body += [' eprintln!("Request failed: {}; timeout: {}", error, error.is_timeout());',
224
+ ' // Inspect these explicitly; server diagnostics can contain sensitive values.',
225
+ ' let _ = (error.server_message(), error.validation_errors());']
226
+ end
227
+ body += [' }', '}']
206
228
  else
207
- body += ["let response = #{call}#{send}?;", 'let _ = response.http_status;', 'let _ = &response.meta;']
229
+ body.concat(result_lines(context, 'response', "#{call}#{send}", 'Request failed'))
230
+ body += ['let _ = response.http_status;', 'let _ = &response.meta;'] if context.detail == :thorough
208
231
  body.concat(output_lines(saved, sample['response']))
209
232
  end
210
- body << '// Request success or a timeout does not establish completion of a server-side operation.'
211
- body << '// Use only the separately documented action-state action; this SDK has no automatic wait helper.'
212
- program(context, mode, body, credentials: action_credentials(saved, context))
233
+ if saved[:desc]['blocking'] == true
234
+ body << '// Request success or a timeout does not establish completion of a server-side operation.'
235
+ if context.detail == :thorough
236
+ body << '// Use only the separately documented action-state action; this API client has no automatic wait helper.'
237
+ end
238
+ end
239
+ credentials = action_credentials(saved, context)
240
+ return program(context, mode, body, credentials:) if context.detail == :thorough
241
+
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")
213
244
  end
214
245
 
215
246
  def action_credentials(action, context)
@@ -218,7 +249,7 @@ module HaveAPI::RustClient
218
249
  live = normalized_members(normalized_members(context.description)['authentication'])
219
250
  saved = @snapshot.fetch('description').fetch('authentication')
220
251
  if live.has_key?('basic') && saved.has_key?('basic')
221
- return ['.basic_auth(std::env::var("HAVEAPI_USER")?, std::env::var("HAVEAPI_PASSWORD")?)']
252
+ return ['.basic_auth("user", "secret")']
222
253
  end
223
254
 
224
255
  %w[token oauth2].each do |provider|
@@ -227,9 +258,9 @@ module HaveAPI::RustClient
227
258
  next unless normalize(normalized_members(advertised)['http_header']) == saved[provider]['http_header']
228
259
 
229
260
  setter = provider == 'token' ? 'token' : 'oauth2_token'
230
- return [".#{setter}(std::env::var(\"HAVEAPI_TOKEN\")?)"]
261
+ return [".#{setter}(#{literal(provider == 'token' ? 'example-token' : 'example-access-token')})"]
231
262
  end
232
- unsupported!('This action requires authentication unavailable in the authorized Rust SDK profile.')
263
+ unsupported!('This action requires authentication unavailable in the authorized Rust API client profile.')
233
264
  end
234
265
 
235
266
  def authorized_action(context)
@@ -251,7 +282,7 @@ module HaveAPI::RustClient
251
282
  mount += '/' unless mount.end_with?('/')
252
283
  unless live['method'] == saved[:desc]['method'] && live['path'] == mount + saved[:route] &&
253
284
  (live['auth'] == false) == (saved[:desc]['auth'] == false)
254
- unsupported!('The Rust SDK route or HTTP method differs from this API action.')
285
+ unsupported!('The Rust API client route or HTTP method differs from this API action.')
255
286
  end
256
287
  %w[input output].each do |direction|
257
288
  saved_io = saved[direction.to_sym]
@@ -260,13 +291,13 @@ module HaveAPI::RustClient
260
291
 
261
292
  unless saved_io && live_io && saved_io[:desc]['layout'] == live_io['layout'] &&
262
293
  saved_io[:desc]['namespace'] == live_io['namespace'] && saved_io[:desc]['format'] == live_io['format']
263
- unsupported!('The Rust SDK input or output layout differs from this API action.')
294
+ unsupported!('The Rust API client input or output layout differs from this API action.')
264
295
  end
265
296
  end
266
297
  %i[global_input object_input].each do |key|
267
298
  next unless saved[key] && saved[key][:parameters].any? { |parameter| parameter[:desc]['required'] == true }
268
299
 
269
- unsupported!('This Rust SDK action requires metadata that is absent from the sample.')
300
+ unsupported!('This Rust API client action requires metadata that is absent from the sample.')
270
301
  end
271
302
  %w[global object].each do |role|
272
303
  if live.dig('meta', role, 'input', 'parameters')&.any? { |_name, parameter| parameter['required'] == true }
@@ -282,7 +313,7 @@ module HaveAPI::RustClient
282
313
  def input_lines(saved, live, request)
283
314
  io = saved[:input]
284
315
  if io.nil?
285
- unsupported!('This Rust SDK action cannot represent the sample input.') unless request.nil? || request == {}
316
+ unsupported!('This Rust API client action cannot represent the sample input.') unless request.nil? || request == {}
286
317
  return { before: [], chain: '' }
287
318
  end
288
319
  list = io[:layout].end_with?('_list')
@@ -293,7 +324,7 @@ module HaveAPI::RustClient
293
324
  request
294
325
  end
295
326
  unless list ? values.is_a?(Array) && values.all?(Hash) : values.is_a?(Hash)
296
- unsupported!('The sample input does not match the Rust SDK layout.')
327
+ unsupported!('The sample input does not match the Rust API client layout.')
297
328
  end
298
329
  objects = list ? values : [values]
299
330
  objects.each { |object| validate_object!(io, live['input'], object, required: true, method: saved[:desc]['method']) }
@@ -321,10 +352,10 @@ module HaveAPI::RustClient
321
352
  parameter = saved_io[:parameters].find { |item| item[:name] == name }
322
353
  live_parameter = live_params && live_params[name]
323
354
  unless parameter && live_parameter && compatible_parameter?(parameter[:desc], live_parameter)
324
- unsupported!('A sample parameter is unavailable or has changed in this Rust SDK.')
355
+ unsupported!('A sample parameter is unavailable or has changed in this Rust API client.')
325
356
  end
326
357
  unless valid_value?(parameter[:desc], value, output: !required)
327
- unsupported!('A sample value cannot be represented by this Rust SDK.')
358
+ unsupported!('A sample value cannot be represented by this Rust API client.')
328
359
  end
329
360
  if method == 'GET' && parameter[:desc]['type'] == 'Custom' && (value.is_a?(Hash) || value.is_a?(Array))
330
361
  unsupported!('Rust GET requests do not support nested Custom query values.')
@@ -334,7 +365,7 @@ module HaveAPI::RustClient
334
365
 
335
366
  [saved_io[:desc].fetch('parameters'), live_params || {}].each do |parameters|
336
367
  if parameters.any? { |name, desc| desc['required'] == true && !object.has_key?(name) }
337
- unsupported!('The sample omits a required Rust SDK input.')
368
+ unsupported!('The sample omits a required Rust API client input.')
338
369
  end
339
370
  end
340
371
  end
@@ -394,18 +425,18 @@ module HaveAPI::RustClient
394
425
  def validate_response!(saved, live, response)
395
426
  io = saved[:output]
396
427
  unless io
397
- unsupported!('The sample response does not match this Rust SDK.') unless response == {}
428
+ unsupported!('The sample response does not match this Rust API client.') unless response == {}
398
429
  return
399
430
  end
400
431
  list = io[:layout].end_with?('_list')
401
432
  unless list ? response.is_a?(Array) && response.all?(Hash) : response.is_a?(Hash)
402
- unsupported!('The sample response does not match this Rust SDK layout.')
433
+ unsupported!('The sample response does not match this Rust API client layout.')
403
434
  end
404
435
  (list ? response : [response]).each { |object| validate_object!(io, live['output'], object, required: false) }
405
436
  end
406
437
 
407
438
  def output_lines(action, response)
408
- return ['let _ = &response.output;'] unless action[:output] && response
439
+ return [] unless action[:output] && response
409
440
 
410
441
  list = action[:output][:layout].end_with?('_list')
411
442
  names = (list ? response : [response]).flat_map(&:keys).uniq
@@ -413,7 +444,7 @@ module HaveAPI::RustClient
413
444
  names.each do |name|
414
445
  parameter = action[:output][:parameters].find { |item| item[:name] == name }
415
446
  target = list ? 'item' : 'response.output'
416
- lines << "#{' ' if list}let _ = &#{target}.#{parameter[:field]};"
447
+ lines << "#{' ' if list}println!(\"{:?}\", &#{target}.#{parameter[:field]});"
417
448
  end
418
449
  lines << '}' if list
419
450
  lines
@@ -1,5 +1,5 @@
1
1
  module HaveAPI
2
2
  module RustClient
3
- VERSION = '0.31.2'.freeze
3
+ VERSION = '0.32.0'.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.31.2
4
+ version: 0.32.0
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.31.2
38
+ version: 0.32.0
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.31.2
45
+ version: 0.32.0
46
46
  - !ruby/object:Gem::Dependency
47
47
  name: tomlrb
48
48
  requirement: !ruby/object:Gem::Requirement