haveapi-rust-client 0.30.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 +7 -0
- data/Gemfile.lock +200 -0
- data/LICENSE.txt +21 -0
- data/README.md +81 -0
- data/bin/haveapi-rust-client +3 -0
- data/docs/documentation.md +103 -0
- data/docs/generation.md +149 -0
- data/docs/releasing.md +61 -0
- data/lib/haveapi/rust_client/cli.rb +55 -0
- data/lib/haveapi/rust_client/documentation.rb +358 -0
- data/lib/haveapi/rust_client/generator.rb +307 -0
- data/lib/haveapi/rust_client/ir.rb +195 -0
- data/lib/haveapi/rust_client/naming.rb +46 -0
- data/lib/haveapi/rust_client/snapshot.rb +207 -0
- data/lib/haveapi/rust_client/version.rb +5 -0
- data/lib/haveapi/rust_client.rb +12 -0
- data/runtime/src/runtime/auth.rs +97 -0
- data/runtime/src/runtime/client.rs +380 -0
- data/runtime/src/runtime/error.rs +134 -0
- data/runtime/src/runtime/mod.rs +19 -0
- data/runtime/src/runtime/protocol.rs +421 -0
- data/runtime/src/runtime/transport.rs +152 -0
- data/runtime/src/runtime/types.rs +219 -0
- data/runtime/src/runtime/validation.rs +213 -0
- data/template/Cargo.toml.erb +19 -0
- data/template/action.rs.erb +103 -0
- data/template/mod.rs.erb +30 -0
- data/template/resource.rs.erb +30 -0
- data/template/resources.rs.erb +4 -0
- metadata +98 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 21033d06951c8de41c052be7cf6d615912ba7fb83e450ed07d360d46691fc880
|
|
4
|
+
data.tar.gz: ace71e7005ca64d911730b47a67795ec64338097bcc3f4116976460c70d5de6b
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: a0de1d3a4d6c2d282e4b68012a43147e3ca865251fe03f179853db9d9b6fe3963fc9950b1a7585457c274a08bb351cc4d073f33385e764649ce4d81e15b96743
|
|
7
|
+
data.tar.gz: 55cebb5a66f6592549da6ccfdb9384d83812373485656e2c2f907da4c3e33618686e5f0fec2b36c2b29af79d1b6fefa7d6d3e126fea6827b719e5d3abbf98226
|
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: ../ruby
|
|
3
|
+
specs:
|
|
4
|
+
haveapi-client (0.30.0)
|
|
5
|
+
activesupport (>= 7.0)
|
|
6
|
+
highline (~> 3.1)
|
|
7
|
+
json
|
|
8
|
+
require_all (~> 2.0.0)
|
|
9
|
+
rest-client (~> 2.1.0)
|
|
10
|
+
ruby-progressbar (~> 1.13.0)
|
|
11
|
+
|
|
12
|
+
PATH
|
|
13
|
+
remote: .
|
|
14
|
+
specs:
|
|
15
|
+
haveapi-rust-client (0.30.0)
|
|
16
|
+
haveapi-client (~> 0.30.0)
|
|
17
|
+
tomlrb (~> 2.0)
|
|
18
|
+
|
|
19
|
+
GEM
|
|
20
|
+
remote: https://rubygems.org/
|
|
21
|
+
specs:
|
|
22
|
+
activesupport (8.1.4)
|
|
23
|
+
base64
|
|
24
|
+
bigdecimal
|
|
25
|
+
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
26
|
+
connection_pool (>= 2.2.5)
|
|
27
|
+
drb
|
|
28
|
+
i18n (>= 1.6, < 2)
|
|
29
|
+
json
|
|
30
|
+
logger (>= 1.4.2)
|
|
31
|
+
minitest (>= 5.1)
|
|
32
|
+
securerandom (>= 0.3)
|
|
33
|
+
tzinfo (~> 2.0, >= 2.0.5)
|
|
34
|
+
uri (>= 0.13.1)
|
|
35
|
+
ast (2.4.3)
|
|
36
|
+
base64 (0.3.0)
|
|
37
|
+
bigdecimal (4.1.3)
|
|
38
|
+
concurrent-ruby (1.3.8)
|
|
39
|
+
connection_pool (3.0.2)
|
|
40
|
+
diff-lcs (1.6.2)
|
|
41
|
+
domain_name (0.7.20261007)
|
|
42
|
+
drb (2.2.3)
|
|
43
|
+
hana (1.3.7)
|
|
44
|
+
highline (3.1.2)
|
|
45
|
+
reline
|
|
46
|
+
http-accept (1.7.0)
|
|
47
|
+
http-cookie (1.1.6)
|
|
48
|
+
domain_name (~> 0.5)
|
|
49
|
+
i18n (1.15.2)
|
|
50
|
+
concurrent-ruby (~> 1.0)
|
|
51
|
+
io-console (0.9.4)
|
|
52
|
+
json (2.21.2)
|
|
53
|
+
json_schemer (2.5.0)
|
|
54
|
+
bigdecimal
|
|
55
|
+
hana (~> 1.3)
|
|
56
|
+
regexp_parser (~> 2.0)
|
|
57
|
+
simpleidn (~> 0.2)
|
|
58
|
+
language_server-protocol (3.17.0.6)
|
|
59
|
+
lint_roller (1.1.0)
|
|
60
|
+
logger (1.7.0)
|
|
61
|
+
mcp (0.25.0)
|
|
62
|
+
json_schemer (>= 2.4)
|
|
63
|
+
mime-types (3.7.0)
|
|
64
|
+
logger
|
|
65
|
+
mime-types-data (~> 3.2025, >= 3.2025.0507)
|
|
66
|
+
mime-types-data (3.2026.1013)
|
|
67
|
+
minitest (6.0.6)
|
|
68
|
+
drb (~> 2.0)
|
|
69
|
+
prism (~> 1.5)
|
|
70
|
+
netrc (0.11.0)
|
|
71
|
+
parallel (1.28.0)
|
|
72
|
+
parser (3.3.12.0)
|
|
73
|
+
ast (~> 2.4.1)
|
|
74
|
+
racc
|
|
75
|
+
prism (1.9.0)
|
|
76
|
+
racc (1.8.1)
|
|
77
|
+
rainbow (3.1.1)
|
|
78
|
+
rake (13.4.2)
|
|
79
|
+
regexp_parser (2.13.1)
|
|
80
|
+
reline (0.7.0)
|
|
81
|
+
io-console (~> 0.5)
|
|
82
|
+
require_all (2.0.0)
|
|
83
|
+
rest-client (2.1.0)
|
|
84
|
+
http-accept (>= 1.7.0, < 2.0)
|
|
85
|
+
http-cookie (>= 1.0.2, < 2.0)
|
|
86
|
+
mime-types (>= 1.16, < 4.0)
|
|
87
|
+
netrc (~> 0.8)
|
|
88
|
+
rspec (3.13.2)
|
|
89
|
+
rspec-core (~> 3.13.0)
|
|
90
|
+
rspec-expectations (~> 3.13.0)
|
|
91
|
+
rspec-mocks (~> 3.13.0)
|
|
92
|
+
rspec-core (3.13.6)
|
|
93
|
+
rspec-support (~> 3.13.0)
|
|
94
|
+
rspec-expectations (3.13.5)
|
|
95
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
96
|
+
rspec-support (~> 3.13.0)
|
|
97
|
+
rspec-mocks (3.13.8)
|
|
98
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
99
|
+
rspec-support (~> 3.13.0)
|
|
100
|
+
rspec-support (3.13.7)
|
|
101
|
+
rubocop (1.85.1)
|
|
102
|
+
json (~> 2.3)
|
|
103
|
+
language_server-protocol (~> 3.17.0.2)
|
|
104
|
+
lint_roller (~> 1.1.0)
|
|
105
|
+
mcp (~> 0.6)
|
|
106
|
+
parallel (~> 1.10)
|
|
107
|
+
parser (>= 3.3.0.2)
|
|
108
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
109
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
|
110
|
+
rubocop-ast (>= 1.49.0, < 2.0)
|
|
111
|
+
ruby-progressbar (~> 1.7)
|
|
112
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
|
113
|
+
rubocop-ast (1.50.0)
|
|
114
|
+
parser (>= 3.3.7.2)
|
|
115
|
+
prism (~> 1.7)
|
|
116
|
+
rubocop-rspec (3.9.0)
|
|
117
|
+
lint_roller (~> 1.1)
|
|
118
|
+
rubocop (~> 1.81)
|
|
119
|
+
ruby-progressbar (1.13.0)
|
|
120
|
+
securerandom (0.4.1)
|
|
121
|
+
simpleidn (0.3.0)
|
|
122
|
+
tomlrb (2.0.5)
|
|
123
|
+
racc (~> 1.4)
|
|
124
|
+
tzinfo (2.0.6)
|
|
125
|
+
concurrent-ruby (~> 1.0)
|
|
126
|
+
unicode-display_width (3.3.0)
|
|
127
|
+
unicode-emoji (~> 4.3)
|
|
128
|
+
unicode-emoji (4.3.0)
|
|
129
|
+
uri (1.1.2)
|
|
130
|
+
|
|
131
|
+
PLATFORMS
|
|
132
|
+
ruby
|
|
133
|
+
x86_64-linux
|
|
134
|
+
|
|
135
|
+
DEPENDENCIES
|
|
136
|
+
haveapi-client!
|
|
137
|
+
haveapi-rust-client!
|
|
138
|
+
rake
|
|
139
|
+
rspec
|
|
140
|
+
rubocop (~> 1.85.0)
|
|
141
|
+
rubocop-rspec
|
|
142
|
+
|
|
143
|
+
CHECKSUMS
|
|
144
|
+
activesupport (8.1.4) sha256=891dd3fe43c4fc8391934b237b1189bb1f661c932a84a840043db9d46a4e80df
|
|
145
|
+
ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
|
|
146
|
+
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
|
|
147
|
+
bigdecimal (4.1.3) sha256=61ebe1e5e559bdc3cc6f2c0ee7f427321fc838f59611c294356eb04d6e21cf66
|
|
148
|
+
bundler (4.0.23) sha256=407ba8a1d928f412847f819aa5f8b6aef7c59fb51feb517112af8a165487287b
|
|
149
|
+
concurrent-ruby (1.3.8) sha256=b2f1be836e968ccc78ccfce277ea79c72a88633f22306782c16ff23fb415d1e1
|
|
150
|
+
connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a
|
|
151
|
+
diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
|
|
152
|
+
domain_name (0.7.20261007) sha256=0976c6ab837dbde991b433d03acc38fdb91fa8839df31a1ed75ce0318dc0441c
|
|
153
|
+
drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373
|
|
154
|
+
hana (1.3.7) sha256=5425db42d651fea08859811c29d20446f16af196308162894db208cac5ce9b0d
|
|
155
|
+
haveapi-client (0.30.0)
|
|
156
|
+
haveapi-rust-client (0.30.0)
|
|
157
|
+
highline (3.1.2) sha256=67cbd34d19f6ef11a7ee1d82ffab5d36dfd5b3be861f450fc1716c7125f4bb4a
|
|
158
|
+
http-accept (1.7.0) sha256=c626860682bfbb3b46462f8c39cd470fd7b0584f61b3cc9df5b2e9eb9972a126
|
|
159
|
+
http-cookie (1.1.6) sha256=ba4b82be64de61dc281243dac70e3c382c45142f20268ed9276a3670c93feaa9
|
|
160
|
+
i18n (1.15.2) sha256=00f9eb62412fe593b2a65a97daa75300d37abb8f7202ec748e94b6d46a9dd1b5
|
|
161
|
+
io-console (0.9.4) sha256=a403304f32928ee85e56d457b4612ca43f2703b561a3258844992bd24085fbb1
|
|
162
|
+
json (2.21.2) sha256=1f1d3b7cf2b3ba1a69beca0bb6db13d5438b80bff3cd54cdaaa620b9b07c1c6a
|
|
163
|
+
json_schemer (2.5.0) sha256=2f01fb4cce721a4e08dd068fc2030cffd0702a7f333f1ea2be6e8991f00ae396
|
|
164
|
+
language_server-protocol (3.17.0.6) sha256=5ef2c0c138f8267e1bc631d3328347d354f96724b0af22f2c79516120443b7f0
|
|
165
|
+
lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
|
|
166
|
+
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
|
|
167
|
+
mcp (0.25.0) sha256=8acf3a5f3b60b6fd6e5d9b8ac5eb85ac504766976cbadaca182d2ffe7d795d98
|
|
168
|
+
mime-types (3.7.0) sha256=dcebf61c246f08e15a4de34e386ebe8233791e868564a470c3fe77c00eed5e56
|
|
169
|
+
mime-types-data (3.2026.1013) sha256=370f152f2a47e54e58829766492ea2cbe130a45358374b8788a6a8ad4e961c96
|
|
170
|
+
minitest (6.0.6) sha256=153ea36d1d987a62942382b61075745042a2b3123b1cd48f4c3675af9cc7d6f1
|
|
171
|
+
netrc (0.11.0) sha256=de1ce33da8c99ab1d97871726cba75151113f117146becbe45aa85cb3dabee3f
|
|
172
|
+
parallel (1.28.0) sha256=33e6de1484baf2524792d178b0913fc8eb94c628d6cfe45599ad4458c638c970
|
|
173
|
+
parser (3.3.12.0) sha256=21a6d7f755d5a24dfbdc6e6b772e4e879a52e7631a88bc5a3a134606052c9828
|
|
174
|
+
prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
|
|
175
|
+
racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
|
|
176
|
+
rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
|
|
177
|
+
rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
|
|
178
|
+
regexp_parser (2.13.1) sha256=5aedb6b7c35688f51e86eef17a15374dfd287c83f4ca644b2c5f3ec50a33b44b
|
|
179
|
+
reline (0.7.0) sha256=5b012d8e55dbf9d450f12bde2cf7d15ff546ae80b3f8f3b30e570d431815583d
|
|
180
|
+
require_all (2.0.0) sha256=68d7b944b0f1c9898a16f9f102b448897c06fd2781d30efec19cbcf6e2164e6a
|
|
181
|
+
rest-client (2.1.0) sha256=35a6400bdb14fae28596618e312776c158f7ebbb0ccad752ff4fa142bf2747e3
|
|
182
|
+
rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587
|
|
183
|
+
rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d
|
|
184
|
+
rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836
|
|
185
|
+
rspec-mocks (3.13.8) sha256=086ad3d3d17533f4237643de0b5c42f04b66348c28bf6b9c2d3f4a3b01af1d47
|
|
186
|
+
rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c
|
|
187
|
+
rubocop (1.85.1) sha256=3dbcf9e961baa4c376eeeb2a03913dca5e3987033b04d38fa538aa1e7406cc77
|
|
188
|
+
rubocop-ast (1.50.0) sha256=b9ca88300da0803ee222ad20cdb30494c0a784eed06fdc35d254b06d662788db
|
|
189
|
+
rubocop-rspec (3.9.0) sha256=8fa70a3619408237d789aeecfb9beef40576acc855173e60939d63332fdb55e2
|
|
190
|
+
ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
|
|
191
|
+
securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
|
|
192
|
+
simpleidn (0.3.0) sha256=12ca730bed2f3db04d11e9bfd1bca3e11fb37f55b21eb2e9793fb5814bf54d03
|
|
193
|
+
tomlrb (2.0.5) sha256=6e9a5e70a94a723842e770fcd14c49867703500d07cd03ebb6c343138a7b4477
|
|
194
|
+
tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b
|
|
195
|
+
unicode-display_width (3.3.0) sha256=4b7aa66a4b11db50f6f7e98411215cd0dd10eaecb665d36eec5bcacb3fa0b613
|
|
196
|
+
unicode-emoji (4.3.0) sha256=11c02fa73290378c066bb0562cd4c87d8e1b706fbbe0059ca12746a4244de8ce
|
|
197
|
+
uri (1.1.2) sha256=89292e98eabbef6248b0a0ed5e7e398e152f83f17560023a6d94a7f492b169fb
|
|
198
|
+
|
|
199
|
+
BUNDLED WITH
|
|
200
|
+
4.0.23
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2014 Jakub Skokan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Rust client generator
|
|
2
|
+
|
|
3
|
+
This Ruby generator turns a saved HaveAPI protocol 2 description into a typed,
|
|
4
|
+
self-contained Rust client for one explicit API version. The generated client
|
|
5
|
+
uses async reqwest by default and offers the same actions through the optional
|
|
6
|
+
`blocking` feature. Ruby and HaveAPI are generation tools, not dependencies of
|
|
7
|
+
the resulting Rust crate.
|
|
8
|
+
|
|
9
|
+
The client contract uses edition 2024, Rust 1.95 or newer, Serde, url 2.5,
|
|
10
|
+
and reqwest 0.13.5 with verified rustls TLS. Generation requires the exact
|
|
11
|
+
formatter version recorded in the client configuration. New test fixtures use
|
|
12
|
+
the formatter from the HaveAPI Nix shell; existing clients retain their own
|
|
13
|
+
recorded formatter. A separate acceptance check verifies Rust 1.95 because the
|
|
14
|
+
development compiler is newer.
|
|
15
|
+
|
|
16
|
+
## Development
|
|
17
|
+
|
|
18
|
+
From the HaveAPI root:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
nix develop .#client-rust
|
|
22
|
+
cd clients/rust
|
|
23
|
+
bundle exec rspec spec/unit
|
|
24
|
+
bundle exec rubocop lib bin spec/unit spec/spec_helper.rb haveapi-rust-client.gemspec
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The component shell installs the dependencies frozen in the tracked
|
|
28
|
+
`clients/rust/Gemfile.lock`. Use its Ruby interpreter with those gems; native
|
|
29
|
+
gems installed by this shell cannot be loaded by a different ambient Ruby.
|
|
30
|
+
The root Git hooks also need the root shell's dependencies: prepare them with
|
|
31
|
+
`nix develop --command true` before committing, then commit with the declared
|
|
32
|
+
Overcommit hooks enabled. Do not install gems concurrently in the same gem home.
|
|
33
|
+
|
|
34
|
+
`make test-rust-unit` runs the generator specs. `make test-rust` additionally
|
|
35
|
+
runs both runtime feature configurations and the generated-client protocol
|
|
36
|
+
harness. Compilation, linting Rust, server integration, MSRV and packaging can
|
|
37
|
+
take longer; use the development workspace's verification procedure when
|
|
38
|
+
working there. Unit specs never start a server or invoke Cargo.
|
|
39
|
+
|
|
40
|
+
## Generate a client
|
|
41
|
+
|
|
42
|
+
Keep acquisition separate from generation:
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
bundle exec haveapi-rust-client fetch https://api.example.test/v1.0/ \
|
|
46
|
+
--api-version 1.0 --profile public --output schema/api-1.0.json
|
|
47
|
+
bundle exec haveapi-rust-client generate --schema schema/api-1.0.json \
|
|
48
|
+
--api-version 1.0 --config generator.toml --output src/generated
|
|
49
|
+
bundle exec haveapi-rust-client generate --schema schema/api-1.0.json \
|
|
50
|
+
--api-version 1.0 --config generator.toml --output src/generated --check
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Run these commands from the consuming project with this generator available in
|
|
54
|
+
its Bundler environment. The explicit URL includes the selected version.
|
|
55
|
+
Neither command selects the server's default API version. Offline generation
|
|
56
|
+
uses only the saved schema, generator configuration, locked Ruby dependencies,
|
|
57
|
+
maintained runtime source and pinned formatter.
|
|
58
|
+
|
|
59
|
+
The handwritten crate entry point is:
|
|
60
|
+
|
|
61
|
+
```rust
|
|
62
|
+
mod generated;
|
|
63
|
+
pub use generated::*;
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
A generated action looks like
|
|
67
|
+
`api.vps().update(123).hostname("example").send().await?`. The blocking client
|
|
68
|
+
uses `blocking::Client` and ends the same chain in `.send()?`. Construct, use and
|
|
69
|
+
drop a blocking client outside an async runtime, or put its entire lifecycle
|
|
70
|
+
inside `tokio::task::spawn_blocking`. See the [runtime contract](runtime/README.md)
|
|
71
|
+
for authentication, errors, deadlines and blocking restrictions.
|
|
72
|
+
|
|
73
|
+
Read the [integration harness guide](spec/integration/README.md) for fixture
|
|
74
|
+
setup and acceptance commands.
|
|
75
|
+
|
|
76
|
+
Read [generation and snapshots](docs/generation.md) for configuration, naming,
|
|
77
|
+
metadata fidelity and ownership rules, and [packaging](docs/releasing.md) before
|
|
78
|
+
preparing a generator or generated-client release.
|
|
79
|
+
|
|
80
|
+
The [server documentation adapter](docs/documentation.md) provides optional
|
|
81
|
+
async and blocking examples for a configured published SDK profile.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Rust examples in server documentation
|
|
2
|
+
|
|
3
|
+
The optional `HaveAPI::RustClient::Documentation` adapter maps a published Rust
|
|
4
|
+
SDK's saved snapshot and TOML configuration to server documentation. It uses the same
|
|
5
|
+
Snapshot validation, complete IR, naming scopes and types as the generator.
|
|
6
|
+
Construction and rendering make no network requests and run no generator, compiler
|
|
7
|
+
or formatter. The server loads its trusted local files at startup.
|
|
8
|
+
|
|
9
|
+
An API operator installs the matching `haveapi-rust-client` gem and configures a
|
|
10
|
+
profile before mounting the server:
|
|
11
|
+
|
|
12
|
+
```ruby
|
|
13
|
+
api.documentation_clients(
|
|
14
|
+
'1.0',
|
|
15
|
+
rust: {
|
|
16
|
+
snapshot_path: '/srv/api/sdk/schema/api-1.0.json',
|
|
17
|
+
generator_config_path: '/srv/api/sdk/generator.toml',
|
|
18
|
+
generator_version: HaveAPI::RustClient::VERSION
|
|
19
|
+
}
|
|
20
|
+
)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Use the exact generator version that produced the published SDK. The profile's
|
|
24
|
+
package name and package version come from `crate_name` and `crate_version` in
|
|
25
|
+
that SDK's existing [generator config](generation.md). They are independent of
|
|
26
|
+
the API and HaveAPI package versions. Rust imports normalize crate-name hyphens
|
|
27
|
+
to underscores. Configure a profile only for an SDK that users can install; the
|
|
28
|
+
examples do not publish or generate that package.
|
|
29
|
+
|
|
30
|
+
The server hides both Rust variants when that API version has no Rust profile.
|
|
31
|
+
Invalid configured profiles fail at startup. A configured profile can still serve examples after unrelated additions to the
|
|
32
|
+
live API. Each rendered sample must match the saved route,
|
|
33
|
+
HTTP method, input and output layouts, and the contracts of the parameters it
|
|
34
|
+
uses. The adapter
|
|
35
|
+
checks the full SDK map against the current user's filtered help and sample;
|
|
36
|
+
it never recomputes names from a reduced schema or prints hidden saved defaults.
|
|
37
|
+
An unavailable action, changed parameter or unsupported value produces a short
|
|
38
|
+
limitation in place of code. Filesystem paths remain outside rendered output.
|
|
39
|
+
|
|
40
|
+
Both variants show a complete program and Cargo dependency comments. Async
|
|
41
|
+
programs use Tokio and `.send().await?`. Blocking programs enable the SDK's
|
|
42
|
+
`blocking` feature and construct, use and drop the client in a synchronous
|
|
43
|
+
`main`, outside async runtime threads. `HAVEAPI_BASE_URL` can override the shown
|
|
44
|
+
mounted root. Authenticated actions use advertised existing credentials from
|
|
45
|
+
`HAVEAPI_USER`/`HAVEAPI_PASSWORD` or `HAVEAPI_TOKEN`; authentication examples never
|
|
46
|
+
invent an OAuth acquisition flow or automatically follow token challenges.
|
|
47
|
+
|
|
48
|
+
Object setters preserve null, zero, false and empty strings, and leave omitted
|
|
49
|
+
fields unset.
|
|
50
|
+
Lists use generated `Input` structs with `InputValue` fields and
|
|
51
|
+
`..Default::default()` for omitted values. GET lists and nested Custom query
|
|
52
|
+
values produce limitations. The example DSL has no metadata input channel, so actions requiring metadata
|
|
53
|
+
show a limitation. Responses expose
|
|
54
|
+
`output`, typed `meta` and `http_status`. Fields accessed in examples must occur
|
|
55
|
+
in the authorized sample. Rust provides explicit action-state calls rather
|
|
56
|
+
than an automatic operation-wait helper. Request success and timeouts do not
|
|
57
|
+
establish whether a server-side operation finished or was rolled back.
|
|
58
|
+
|
|
59
|
+
## Verification
|
|
60
|
+
|
|
61
|
+
The pure adapter specs and server renderer bridge specs run before compilation:
|
|
62
|
+
|
|
63
|
+
```sh
|
|
64
|
+
nix develop .#client-rust --command bash -c \
|
|
65
|
+
'cd clients/rust && bundle exec rspec spec/unit/documentation_spec.rb spec/unit/documentation_harness_spec.rb'
|
|
66
|
+
nix develop .#server-ruby --command bash -c \
|
|
67
|
+
'cd servers/ruby && bundle exec rspec spec/client_examples/rust_client_spec.rb'
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The acceptance runner extracts actual `RustAsyncClient.render` and
|
|
71
|
+
`RustBlockingClient.render` source. It generates a matching temporary fixture
|
|
72
|
+
SDK, compiles the emitted imports, paths, methods and types, and executes the
|
|
73
|
+
programs against the existing disposable HaveAPI fixture at `/` and
|
|
74
|
+
`/mounted/api/`. It adds output assertions without replacing emitted requests.
|
|
75
|
+
Authentication checks compose the emitted authentication builder with the
|
|
76
|
+
emitted action body. It tests both modes with the development compiler and
|
|
77
|
+
Rust 1.95, including zero/false/empty/null/omitted inputs, nested Custom, lists,
|
|
78
|
+
nested resources, failure branches, metadata, existing credentials and an
|
|
79
|
+
action-state reference. The fixture uses local test credentials and in-memory
|
|
80
|
+
data.
|
|
81
|
+
|
|
82
|
+
Prepare the Rust/server environments as described in the
|
|
83
|
+
[integration guide](../spec/integration/README.md). Supply absolute paths to
|
|
84
|
+
the independently selected Rust 1.95 Cargo and compiler executables:
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
HAVEAPI_RUST_DOCUMENTATION=1 \
|
|
88
|
+
HAVEAPI_RUST_MSRV_CARGO=/path/to/rust-1.95/bin/cargo \
|
|
89
|
+
HAVEAPI_RUST_MSRV_RUSTC=/path/to/rust-1.95/bin/rustc \
|
|
90
|
+
HAVEAPI_RUST_DOCUMENTATION_EVIDENCE=/path/to/new-evidence-directory \
|
|
91
|
+
nix develop .#client-rust --command ruby clients/rust/spec/documentation/run.rb
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The optional `HAVEAPI_RUST_DOCUMENTATION_EVIDENCE` directory retains separate
|
|
95
|
+
`root` and `mounted-api` receipts: snapshot, generator TOML, Cargo manifest and
|
|
96
|
+
lockfile, generated source and ownership manifest, emitted programs and selected
|
|
97
|
+
tool paths/versions. Choose a fresh directory; existing mount receipts cause an
|
|
98
|
+
error. Build outputs and caches remain temporary. A failed run retains whichever
|
|
99
|
+
fixture artifacts were available before the failure.
|
|
100
|
+
|
|
101
|
+
Ordinary unit specs do not start this runner. Temporary generated SDK/source,
|
|
102
|
+
Cargo outputs and the owned server are removed by their native runner cleanup;
|
|
103
|
+
failed server startup retains the integration launcher's private diagnostic log.
|
data/docs/generation.md
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# Snapshots and generation
|
|
2
|
+
|
|
3
|
+
Each snapshot describes the surface visible to its capture profile for a single
|
|
4
|
+
API version. It cannot establish that permission-filtered or plugin-specific
|
|
5
|
+
resources are complete. Inventory the captured resources, actions and parameters
|
|
6
|
+
and record unknown coverage explicitly. Do not combine unrelated users' schemas
|
|
7
|
+
or add fixture-only descriptions to a production snapshot.
|
|
8
|
+
|
|
9
|
+
## Snapshot format
|
|
10
|
+
|
|
11
|
+
The saved JSON envelope contains exactly these keys:
|
|
12
|
+
|
|
13
|
+
| Key | Meaning |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| `schema_format` | Snapshot format, currently `1` |
|
|
16
|
+
| `api_version` | Explicit API version string |
|
|
17
|
+
| `protocol_version` | HaveAPI protocol version, currently major `2` |
|
|
18
|
+
| `source_url` | Credential-free URL of the explicit version endpoint |
|
|
19
|
+
| `profile` | Non-secret capture profile label |
|
|
20
|
+
| `mount_path` | Server mount root, including leading and trailing `/` |
|
|
21
|
+
| `description` | Sanitized HaveAPI description |
|
|
22
|
+
|
|
23
|
+
`Snapshot.canonical` sorts object keys, indents by two spaces and writes UTF-8
|
|
24
|
+
with LF endings and a terminal newline. Array order remains meaningful. The
|
|
25
|
+
schema SHA-256 covers those exact bytes; it identifies the generated interface
|
|
26
|
+
in addition to the API version.
|
|
27
|
+
|
|
28
|
+
The sanitizer removes examples, labels, descriptions, validator messages and
|
|
29
|
+
protected default values. It preserves parameter and choice keys, machine
|
|
30
|
+
validators, ordinary defaults, metadata, auth headers, public OAuth discovery
|
|
31
|
+
and token action schemas. A field named `default`, `description` or `token`
|
|
32
|
+
remains a field. Protected defaults produce diagnostics without their values;
|
|
33
|
+
callers save these in separate provenance. Review remaining defaults before
|
|
34
|
+
publishing a snapshot. Never save credentials, HTTP headers/cookies or login
|
|
35
|
+
responses in a snapshot or provenance record.
|
|
36
|
+
|
|
37
|
+
The library entry points are:
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
snapshot = HaveAPI::RustClient::Snapshot.fetch(
|
|
41
|
+
url: 'https://api.example.test/v1.0/', api_version: '1.0', profile: 'public'
|
|
42
|
+
)
|
|
43
|
+
snapshot = HaveAPI::RustClient::Snapshot.load('schema/api-1.0.json')
|
|
44
|
+
inventory = HaveAPI::RustClient::IR.new(snapshot).inventory
|
|
45
|
+
HaveAPI::RustClient::Generator.new(schema: snapshot, config: config)
|
|
46
|
+
.generate(output: 'src/generated', check: true)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Capture tools that already hold an OPTIONS envelope can use
|
|
50
|
+
`Snapshot.from_envelope(envelope, api_version:, source_url:, profile:,
|
|
51
|
+
mount_path: '/', diagnostics: [])`. This applies the same sanitizer used by
|
|
52
|
+
fetch. Pass a diagnostics array to retain removal notices in provenance.
|
|
53
|
+
Acquisition timestamps, raw capture hashes, the source revision, resolved
|
|
54
|
+
Ruby/Cargo locks and exact Rust toolchain belong in separate provenance.
|
|
55
|
+
The generated ownership manifest records schema, runtime and generator-source
|
|
56
|
+
digests, Ruby lock digest, configuration and the owned file inventory.
|
|
57
|
+
|
|
58
|
+
## Configuration and crate scaffold
|
|
59
|
+
|
|
60
|
+
`generator.toml` has these root keys:
|
|
61
|
+
|
|
62
|
+
```toml
|
|
63
|
+
crate_name = "example-client"
|
|
64
|
+
crate_version = "0.1.0"
|
|
65
|
+
license = "MIT"
|
|
66
|
+
edition = "2024"
|
|
67
|
+
rust_version = "1.95"
|
|
68
|
+
rustfmt_version = "rustfmt 1.9.0"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The crate version is independent of the API version. This generator supports
|
|
72
|
+
the displayed license, edition and MSRV. The formatter version above is an
|
|
73
|
+
example; record the exact `rustfmt --version` output for the client's chosen
|
|
74
|
+
formatter. `RUSTFMT` can select its executable. Generation rejects a version
|
|
75
|
+
that differs from the configuration.
|
|
76
|
+
|
|
77
|
+
`Generator#manifest_requirements` renders the initial Cargo manifest template.
|
|
78
|
+
It declares serde 1 with derive, serde_json 1, url 2.5, and reqwest 0.13.5 with
|
|
79
|
+
`default-features = false` and `features = ["json", "rustls"]`. Features are
|
|
80
|
+
`default = []` and `blocking = ["reqwest/blocking"]`. Tokio 1 with macros and
|
|
81
|
+
rt-multi-thread supports tests/examples. Commit the consuming crate's Cargo.lock.
|
|
82
|
+
Regeneration checks these requirements when the output is `src/generated`
|
|
83
|
+
inside an existing crate. It reports mismatches and never replaces Cargo.toml
|
|
84
|
+
or other handwritten package files.
|
|
85
|
+
|
|
86
|
+
## Generated interface
|
|
87
|
+
|
|
88
|
+
Resource components normalize to snake_case and join with double underscores.
|
|
89
|
+
For example, nested `project/task` becomes `project__task`, while the top-level
|
|
90
|
+
resource `project_task` keeps that name. A flat accessor binds ancestor path
|
|
91
|
+
arguments: `api.project__task(project_id).show(task_id)`. Arguments follow the
|
|
92
|
+
path template's order and accept values implementing `ToString`; the runtime
|
|
93
|
+
encodes each value as a single path segment. All remaining name collisions fail
|
|
94
|
+
with both original schema paths. Rust keywords receive a trailing underscore.
|
|
95
|
+
Action aliases use the same request and models.
|
|
96
|
+
|
|
97
|
+
Action modules live at `resources::<resource_path>::<action>`. They expose
|
|
98
|
+
`Input`, `Output`, `Meta` and `SPEC` where applicable. Object/hash input has a
|
|
99
|
+
unique defaultable struct with public `InputValue<T>` fields. Its initial state
|
|
100
|
+
is Unset. Value setters preserve zero, false and empty strings; nullable fields
|
|
101
|
+
also have `_null()` setters. Omitted fields stay omitted so server defaults can
|
|
102
|
+
apply. List input uses `Vec<Input>` through `.input(...)`, rather than setters
|
|
103
|
+
that could ambiguously apply to multiple items.
|
|
104
|
+
|
|
105
|
+
The request validates input before JSON serialization, then the runtime checks
|
|
106
|
+
required, nullable and typed values before dispatch. Datetime inputs are strings
|
|
107
|
+
validated using HaveAPI's ISO forms. Resource input is a nonnegative numeric ID;
|
|
108
|
+
portable metadata does not describe arbitrary string primary keys. Server-side
|
|
109
|
+
validators remain authoritative; the generator retains their metadata without
|
|
110
|
+
attempting to translate arbitrary Ruby regular expressions into Rust.
|
|
111
|
+
|
|
112
|
+
All ordinary output fields are optional, including fields marked required in
|
|
113
|
+
the description, because authorization can hide them. Missing and null output
|
|
114
|
+
fields both become `None`; wrong field types remain decoding errors. Extra
|
|
115
|
+
fields and metadata are retained. Custom values use `serde_json::Value`.
|
|
116
|
+
Associations retain their partial JSON maps and documented ID/label keys; they
|
|
117
|
+
do not imply a complete Show response or trigger a network request.
|
|
118
|
+
|
|
119
|
+
GET uses bracketed scalar query inputs. Explicit nullable null becomes an empty
|
|
120
|
+
query value, so nullable empty strings cannot always be distinguished from null
|
|
121
|
+
in GET requests. Nested Custom values and list layouts in GET queries fail
|
|
122
|
+
locally. DELETE, POST, PUT and PATCH send JSON object bodies, preserving input
|
|
123
|
+
and metadata namespaces. Body input supports objects and named lists, including
|
|
124
|
+
nested Custom values and explicit JSON null; an empty assembled body is `{}`.
|
|
125
|
+
Ordinary output `namespace=false` and unnamespaced list input are rejected
|
|
126
|
+
rather than guessed. Named empty object/hash output permits the server's
|
|
127
|
+
`response: {}` shortcut; missing namespaces remain errors for nonempty or list
|
|
128
|
+
outputs.
|
|
129
|
+
|
|
130
|
+
## Owned output and recovery
|
|
131
|
+
|
|
132
|
+
The generator owns only the requested output directory. It stages and formats
|
|
133
|
+
files before installation and records ownership in `.haveapi-generated.json`.
|
|
134
|
+
Existing nonempty output needs this marker; foreign files and symlinks are
|
|
135
|
+
refused. Regeneration removes obsolete owned files and preserves siblings such
|
|
136
|
+
as the handwritten facade and Cargo.toml. Never put handwritten files inside
|
|
137
|
+
the generated directory.
|
|
138
|
+
|
|
139
|
+
First installation uses an atomic directory rename. Replacing nonempty output
|
|
140
|
+
uses Linux `renameat2` directory exchange; hosts without that operation receive
|
|
141
|
+
an error before replacement. `--check` compares a temporary candidate without
|
|
142
|
+
writing the destination, its parent, or package files. A stale check exits with
|
|
143
|
+
an error and leaves existing contents and timestamps intact.
|
|
144
|
+
|
|
145
|
+
Keep runtime fixes in HaveAPI, regenerate each affected client from its retained
|
|
146
|
+
snapshot, and release that client through its own workflow. Copied runtime
|
|
147
|
+
source matches `runtime/src/runtime` byte-for-byte and has no path or Git runtime
|
|
148
|
+
dependency. Reverting a client revision restores its previous generated surface;
|
|
149
|
+
it cannot undo API actions that a newer client already sent.
|
data/docs/releasing.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Packaging the Rust generator and generated clients
|
|
2
|
+
|
|
3
|
+
The Ruby gem is `haveapi-rust-client` and follows HaveAPI's shared VERSION.
|
|
4
|
+
`make version` updates its version, client dependency and tracked generator lock
|
|
5
|
+
alongside existing components. The top-level release build includes this gem,
|
|
6
|
+
and the protected publishing job uploads it after `haveapi-client`. Configure
|
|
7
|
+
its RubyGems publisher before release; a new gem can use a pending publisher
|
|
8
|
+
without a manual seed upload. Follow the
|
|
9
|
+
[shared release procedure](../../../doc/agent-instructions/releases.md) for
|
|
10
|
+
registry setup, approval, retained artifacts and recovery.
|
|
11
|
+
|
|
12
|
+
Prepare its package from `clients/rust` in the component Nix shell:
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
bundle exec rake build
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The gem includes Ruby/ERB sources, the executable, documentation, Ruby dependency
|
|
19
|
+
lock and maintained runtime source. It excludes the private Cargo harness and
|
|
20
|
+
integration fixtures. The top-level `make release` builds and inspects this gem,
|
|
21
|
+
retains its checksum and source in the shared manifest, and runs an isolated
|
|
22
|
+
installed-package generation check. To repeat that check against retained
|
|
23
|
+
artifacts in the root Nix shell:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
ruby utils/release/rust_smoke.rb dist
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The check loads the generator from the package and verifies its copied runtime.
|
|
30
|
+
Run the broader generator, runtime and generated-client checks as well.
|
|
31
|
+
`make test-rust-unit` runs the quick generator specs; `make test-rust` covers
|
|
32
|
+
those specs, both runtime feature configurations
|
|
33
|
+
and generated-client protocol acceptance. Top-level `make test` includes that
|
|
34
|
+
Rust target. Publishing a new gem version requires separate authorization and
|
|
35
|
+
approval in the protected release environment; `make publish` exits without
|
|
36
|
+
publishing.
|
|
37
|
+
|
|
38
|
+
Generated Rust crates have their own SemVer and Cargo.lock. Their API-version
|
|
39
|
+
constant and schema digest identify the target independently of the crate
|
|
40
|
+
version. Adding required input, changing public types or removing actions can
|
|
41
|
+
require a breaking crate release; increasing the MSRV is a deliberate release
|
|
42
|
+
decision. Extra response fields and unknown choice values remain compatible
|
|
43
|
+
through optional fields and open string values.
|
|
44
|
+
|
|
45
|
+
The embedded runtime has no separate registry release. Its `haveapi-runtime-tests`
|
|
46
|
+
Cargo package is a private development harness with `publish = false`, not an
|
|
47
|
+
artifact of HaveAPI's shared version. No crates.io token is needed to release
|
|
48
|
+
the generator gem. Publish a generated crate through its consuming project's
|
|
49
|
+
own release procedure and registry identity.
|
|
50
|
+
|
|
51
|
+
Before shipping a generated client, verify deterministic offline regeneration
|
|
52
|
+
and read-only `--check`, formatting, async and blocking library/examples, protocol
|
|
53
|
+
fixtures, the declared minimum Rust version and `cargo package`. Inspect the
|
|
54
|
+
archive for copied runtime source and ordinary registry dependencies. No Ruby,
|
|
55
|
+
HaveAPI checkout or private runtime crate should be needed to build it.
|
|
56
|
+
|
|
57
|
+
A runtime source fix reaches consumers only after regeneration and release of
|
|
58
|
+
each affected generated crate. Keep the prior schema, generator revision,
|
|
59
|
+
lockfiles and generated revision available for recovery. Pinning a previous
|
|
60
|
+
crate version can restore software behavior but cannot reverse server changes
|
|
61
|
+
made by an API action.
|