@@ -71,29 +71,6 @@ impl ApiClient {
7171 }
7272 }
7373
74- fn debug_headers ( & self ) -> Vec < ( & str , String ) > {
75- let masked = if self . api_key . len ( ) > 4 {
76- format ! ( "Bearer ...{}" , & self . api_key[ self . api_key. len( ) -4 ..] )
77- } else {
78- "Bearer ***" . to_string ( )
79- } ;
80- let mut headers = vec ! [ ( "Authorization" , masked) ] ;
81- if let Some ( ref ws) = self . workspace_id {
82- headers. push ( ( "X-Workspace-Id" , ws. clone ( ) ) ) ;
83- }
84- if let Some ( ref sid) = self . sandbox_id {
85- // Send both headers during the session→sandbox migration window.
86- headers. push ( ( "X-Session-Id" , sid. clone ( ) ) ) ;
87- headers. push ( ( "X-Sandbox-Id" , sid. clone ( ) ) ) ;
88- }
89- headers
90- }
91-
92- fn log_request ( & self , method : & str , url : & str , body : Option < & serde_json:: Value > ) {
93- let headers = self . debug_headers ( ) ;
94- let header_refs: Vec < ( & str , & str ) > = headers. iter ( ) . map ( |( k, v) | ( * k, v. as_str ( ) ) ) . collect ( ) ;
95- util:: debug_request ( method, url, & header_refs, body) ;
96- }
9774
9875 /// Prints an error for a non-2xx response and exits. On 4xx, first re-probes
9976 /// the API key: if it's actually invalid, a clear re-auth hint is shown
@@ -122,29 +99,25 @@ impl ApiClient {
12299 req
123100 }
124101
125- /// GET request with query parameters, returns parsed response.
126- /// Parameters with `None` values are omitted.
127- pub fn get_with_params < T : DeserializeOwned > ( & self , path : & str , params : & [ ( & str , Option < String > ) ] ) -> T {
128- let filtered: Vec < ( & str , & String ) > = params. iter ( )
129- . filter_map ( |( k, v) | v. as_ref ( ) . map ( |val| ( * k, val) ) )
130- . collect ( ) ;
131- let url = format ! ( "{}{path}" , self . api_url) ;
132- self . log_request ( "GET" , & url, None ) ;
133-
134- let resp = match self . build_request ( reqwest:: Method :: GET , & url) . query ( & filtered) . send ( ) {
135- Ok ( r) => r,
102+ /// Send via `util::send_debug` and unwrap connection errors with the
103+ /// CLI's standard "error connecting" exit. All public HTTP methods
104+ /// route through here so debug logging is uniform.
105+ fn send (
106+ & self ,
107+ builder : reqwest:: blocking:: RequestBuilder ,
108+ body_for_log : Option < & serde_json:: Value > ,
109+ ) -> ( reqwest:: StatusCode , String ) {
110+ match util:: send_debug ( & self . client , builder, body_for_log) {
111+ Ok ( pair) => pair,
136112 Err ( e) => {
137113 eprintln ! ( "error connecting to API: {e}" ) ;
138114 std:: process:: exit ( 1 ) ;
139115 }
140- } ;
141-
142- let ( status, body) = util:: debug_response ( resp) ;
143- if !status. is_success ( ) {
144- self . fail_response ( status, body) ;
145116 }
117+ }
146118
147- match serde_json:: from_str ( & body) {
119+ fn parse_json < T : DeserializeOwned > ( body : & str ) -> T {
120+ match serde_json:: from_str ( body) {
148121 Ok ( v) => v,
149122 Err ( e) => {
150123 eprintln ! ( "error parsing response: {e}" ) ;
@@ -153,159 +126,83 @@ impl ApiClient {
153126 }
154127 }
155128
156- /// GET request, returns parsed response.
157- pub fn get < T : DeserializeOwned > ( & self , path : & str ) -> T {
129+ /// GET request with query parameters, returns parsed response.
130+ /// Parameters with `None` values are omitted.
131+ pub fn get_with_params < T : DeserializeOwned > ( & self , path : & str , params : & [ ( & str , Option < String > ) ] ) -> T {
132+ let filtered: Vec < ( & str , & String ) > = params. iter ( )
133+ . filter_map ( |( k, v) | v. as_ref ( ) . map ( |val| ( * k, val) ) )
134+ . collect ( ) ;
158135 let url = format ! ( "{}{path}" , self . api_url) ;
159- self . log_request ( "GET" , & url, None ) ;
160-
161- let resp = match self . build_request ( reqwest:: Method :: GET , & url) . send ( ) {
162- Ok ( r) => r,
163- Err ( e) => {
164- eprintln ! ( "error connecting to API: {e}" ) ;
165- std:: process:: exit ( 1 ) ;
166- }
167- } ;
168-
169- let ( status, body) = util:: debug_response ( resp) ;
136+ let req = self . build_request ( reqwest:: Method :: GET , & url) . query ( & filtered) ;
137+ let ( status, body) = self . send ( req, None ) ;
170138 if !status. is_success ( ) {
171139 self . fail_response ( status, body) ;
172140 }
141+ Self :: parse_json ( & body)
142+ }
173143
174- match serde_json:: from_str ( & body) {
175- Ok ( v) => v,
176- Err ( e) => {
177- eprintln ! ( "error parsing response: {e}" ) ;
178- std:: process:: exit ( 1 ) ;
179- }
144+ /// GET request, returns parsed response.
145+ pub fn get < T : DeserializeOwned > ( & self , path : & str ) -> T {
146+ let url = format ! ( "{}{path}" , self . api_url) ;
147+ let req = self . build_request ( reqwest:: Method :: GET , & url) ;
148+ let ( status, body) = self . send ( req, None ) ;
149+ if !status. is_success ( ) {
150+ self . fail_response ( status, body) ;
180151 }
152+ Self :: parse_json ( & body)
181153 }
182154
183155 /// GET request; returns `None` on HTTP 404. Other status codes use the same handling as
184156 /// [`Self::get`]. Used when probing many paths where a missing resource is normal.
185157 pub fn get_none_if_not_found < T : DeserializeOwned > ( & self , path : & str ) -> Option < T > {
186158 let url = format ! ( "{}{path}" , self . api_url) ;
187- self . log_request ( "GET" , & url, None ) ;
188-
189- let resp = match self . build_request ( reqwest:: Method :: GET , & url) . send ( ) {
190- Ok ( r) => r,
191- Err ( e) => {
192- eprintln ! ( "error connecting to API: {e}" ) ;
193- std:: process:: exit ( 1 ) ;
194- }
195- } ;
196-
197- let ( status, body) = util:: debug_response ( resp) ;
159+ let req = self . build_request ( reqwest:: Method :: GET , & url) ;
160+ let ( status, body) = self . send ( req, None ) ;
198161 if status == reqwest:: StatusCode :: NOT_FOUND {
199162 return None ;
200163 }
201164 if !status. is_success ( ) {
202165 self . fail_response ( status, body) ;
203166 }
204-
205- match serde_json:: from_str ( & body) {
206- Ok ( v) => Some ( v) ,
207- Err ( e) => {
208- eprintln ! ( "error parsing response: {e}" ) ;
209- std:: process:: exit ( 1 ) ;
210- }
211- }
167+ Some ( Self :: parse_json ( & body) )
212168 }
213169
214170 /// POST request with JSON body, returns parsed response.
215171 pub fn post < T : DeserializeOwned > ( & self , path : & str , body : & serde_json:: Value ) -> T {
216172 let url = format ! ( "{}{path}" , self . api_url) ;
217- self . log_request ( "POST" , & url, Some ( body) ) ;
218-
219- let resp = match self . build_request ( reqwest:: Method :: POST , & url)
220- . json ( body)
221- . send ( )
222- {
223- Ok ( r) => r,
224- Err ( e) => {
225- eprintln ! ( "error connecting to API: {e}" ) ;
226- std:: process:: exit ( 1 ) ;
227- }
228- } ;
229-
230- let ( status, resp_body) = util:: debug_response ( resp) ;
173+ let req = self . build_request ( reqwest:: Method :: POST , & url) . json ( body) ;
174+ let ( status, resp_body) = self . send ( req, Some ( body) ) ;
231175 if !status. is_success ( ) {
232176 self . fail_response ( status, resp_body) ;
233177 }
234-
235- match serde_json:: from_str ( & resp_body) {
236- Ok ( v) => v,
237- Err ( e) => {
238- eprintln ! ( "error parsing response: {e}" ) ;
239- std:: process:: exit ( 1 ) ;
240- }
241- }
178+ Self :: parse_json ( & resp_body)
242179 }
243180
244181 /// GET request, exits only on connection error, returns raw (status, body).
245182 /// Use for best-effort endpoints (e.g. health checks) where the caller wants
246183 /// to handle non-2xx responses gracefully instead of aborting.
247184 pub fn get_raw ( & self , path : & str ) -> ( reqwest:: StatusCode , String ) {
248185 let url = format ! ( "{}{path}" , self . api_url) ;
249- self . log_request ( "GET" , & url, None ) ;
250-
251- let resp = match self . build_request ( reqwest:: Method :: GET , & url) . send ( ) {
252- Ok ( r) => r,
253- Err ( e) => {
254- eprintln ! ( "error connecting to API: {e}" ) ;
255- std:: process:: exit ( 1 ) ;
256- }
257- } ;
258-
259- util:: debug_response ( resp)
186+ let req = self . build_request ( reqwest:: Method :: GET , & url) ;
187+ self . send ( req, None )
260188 }
261189
262190 /// POST request with JSON body, exits on error, returns raw (status, body).
263191 pub fn post_raw ( & self , path : & str , body : & serde_json:: Value ) -> ( reqwest:: StatusCode , String ) {
264192 let url = format ! ( "{}{path}" , self . api_url) ;
265- self . log_request ( "POST" , & url, Some ( body) ) ;
266-
267- let resp = match self . build_request ( reqwest:: Method :: POST , & url)
268- . json ( body)
269- . send ( )
270- {
271- Ok ( r) => r,
272- Err ( e) => {
273- eprintln ! ( "error connecting to API: {e}" ) ;
274- std:: process:: exit ( 1 ) ;
275- }
276- } ;
277-
278- util:: debug_response ( resp)
193+ let req = self . build_request ( reqwest:: Method :: POST , & url) . json ( body) ;
194+ self . send ( req, Some ( body) )
279195 }
280196
281197 /// PATCH request with JSON body, returns parsed response.
282198 pub fn patch < T : DeserializeOwned > ( & self , path : & str , body : & serde_json:: Value ) -> T {
283199 let url = format ! ( "{}{path}" , self . api_url) ;
284- self . log_request ( "PATCH" , & url, Some ( body) ) ;
285-
286- let resp = match self . build_request ( reqwest:: Method :: PATCH , & url)
287- . json ( body)
288- . send ( )
289- {
290- Ok ( r) => r,
291- Err ( e) => {
292- eprintln ! ( "error connecting to API: {e}" ) ;
293- std:: process:: exit ( 1 ) ;
294- }
295- } ;
296-
297- let ( status, resp_body) = util:: debug_response ( resp) ;
200+ let req = self . build_request ( reqwest:: Method :: PATCH , & url) . json ( body) ;
201+ let ( status, resp_body) = self . send ( req, Some ( body) ) ;
298202 if !status. is_success ( ) {
299203 self . fail_response ( status, resp_body) ;
300204 }
301-
302- match serde_json:: from_str ( & resp_body) {
303- Ok ( v) => v,
304- Err ( e) => {
305- eprintln ! ( "error parsing response: {e}" ) ;
306- std:: process:: exit ( 1 ) ;
307- }
308- }
205+ Self :: parse_json ( & resp_body)
309206 }
310207
311208 /// POST with a custom request body (for file uploads). Returns raw status and body.
@@ -317,24 +214,16 @@ impl ApiClient {
317214 content_length : Option < u64 > ,
318215 ) -> ( reqwest:: StatusCode , String ) {
319216 let url = format ! ( "{}{path}" , self . api_url) ;
320- self . log_request ( "POST" , & url, None ) ;
321-
322217 let mut req = self . build_request ( reqwest:: Method :: POST , & url)
323218 . header ( "Content-Type" , content_type) ;
324-
325219 if let Some ( len) = content_length {
326220 req = req. header ( "Content-Length" , len) ;
327221 }
328-
329- let resp = match req. body ( reqwest:: blocking:: Body :: new ( reader) ) . send ( ) {
330- Ok ( r) => r,
331- Err ( e) => {
332- eprintln ! ( "error connecting to API: {e}" ) ;
333- std:: process:: exit ( 1 ) ;
334- }
335- } ;
336-
337- util:: debug_response ( resp)
222+ let req = req. body ( reqwest:: blocking:: Body :: new ( reader) ) ;
223+ // Body is an opaque stream — nothing meaningful to print under
224+ // --debug, so pass `None`. Headers (including the masked
225+ // Authorization) still log.
226+ self . send ( req, None )
338227 }
339228
340229}
0 commit comments