@@ -39,7 +39,10 @@ impl ApiClient {
3939 Ok ( t) => t,
4040 Err ( e) => {
4141 eprintln ! ( "{}" , format!( "error: {e}" ) . red( ) ) ;
42- eprintln ! ( "Run {} to log in, or pass --api-key." , "hotdata auth" . cyan( ) ) ;
42+ eprintln ! (
43+ "Run {} to log in, or pass --api-key." ,
44+ "hotdata auth" . cyan( )
45+ ) ;
4346 std:: process:: exit ( 1 ) ;
4447 }
4548 } ;
@@ -71,22 +74,32 @@ impl ApiClient {
7174 }
7275 }
7376
74-
7577 /// Prints an error for a non-2xx response and exits. On 4xx, first re-probes
7678 /// the API key: if it's actually invalid, a clear re-auth hint is shown
7779 /// instead of whatever cryptic body the primary endpoint returned.
7880 fn fail_response ( & self , status : reqwest:: StatusCode , body : String ) -> ! {
7981 let auth_status = if status. is_client_error ( ) {
80- config:: load ( "default" ) . ok ( ) . map ( |pc| auth:: check_status ( & pc) )
82+ config:: load ( "default" )
83+ . ok ( )
84+ . map ( |pc| auth:: check_status ( & pc) )
8185 } else {
8286 None
8387 } ;
84- eprintln ! ( "{}" , format_fail_message( status, & body, auth_status. as_ref( ) ) . red( ) ) ;
88+ eprintln ! (
89+ "{}" ,
90+ format_fail_message( status, & body, auth_status. as_ref( ) ) . red( )
91+ ) ;
8592 std:: process:: exit ( 1 ) ;
8693 }
8794
88- fn build_request ( & self , method : reqwest:: Method , url : & str ) -> reqwest:: blocking:: RequestBuilder {
89- let mut req = self . client . request ( method, url)
95+ fn build_request (
96+ & self ,
97+ method : reqwest:: Method ,
98+ url : & str ,
99+ ) -> reqwest:: blocking:: RequestBuilder {
100+ let mut req = self
101+ . client
102+ . request ( method, url)
90103 . header ( "Authorization" , format ! ( "Bearer {}" , self . api_key) ) ;
91104 if let Some ( ref ws) = self . workspace_id {
92105 req = req. header ( "X-Workspace-Id" , ws) ;
@@ -128,12 +141,19 @@ impl ApiClient {
128141
129142 /// GET request with query parameters, returns parsed response.
130143 /// 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 ( )
144+ pub fn get_with_params < T : DeserializeOwned > (
145+ & self ,
146+ path : & str ,
147+ params : & [ ( & str , Option < String > ) ] ,
148+ ) -> T {
149+ let filtered: Vec < ( & str , & String ) > = params
150+ . iter ( )
133151 . filter_map ( |( k, v) | v. as_ref ( ) . map ( |val| ( * k, val) ) )
134152 . collect ( ) ;
135153 let url = format ! ( "{}{path}" , self . api_url) ;
136- let req = self . build_request ( reqwest:: Method :: GET , & url) . query ( & filtered) ;
154+ let req = self
155+ . build_request ( reqwest:: Method :: GET , & url)
156+ . query ( & filtered) ;
137157 let ( status, body) = self . send ( req, None ) ;
138158 if !status. is_success ( ) {
139159 self . fail_response ( status, body) ;
@@ -205,6 +225,17 @@ impl ApiClient {
205225 Self :: parse_json ( & resp_body)
206226 }
207227
228+ /// PUT request with JSON body, returns parsed response.
229+ pub fn put < T : DeserializeOwned > ( & self , path : & str , body : & serde_json:: Value ) -> T {
230+ let url = format ! ( "{}{path}" , self . api_url) ;
231+ let req = self . build_request ( reqwest:: Method :: PUT , & url) . json ( body) ;
232+ let ( status, resp_body) = self . send ( req, Some ( body) ) ;
233+ if !status. is_success ( ) {
234+ self . fail_response ( status, resp_body) ;
235+ }
236+ Self :: parse_json ( & resp_body)
237+ }
238+
208239 /// POST with a custom request body (for file uploads). Returns raw status and body.
209240 pub fn post_body < R : std:: io:: Read + Send + ' static > (
210241 & self ,
@@ -214,7 +245,8 @@ impl ApiClient {
214245 content_length : Option < u64 > ,
215246 ) -> ( reqwest:: StatusCode , String ) {
216247 let url = format ! ( "{}{path}" , self . api_url) ;
217- let mut req = self . build_request ( reqwest:: Method :: POST , & url)
248+ let mut req = self
249+ . build_request ( reqwest:: Method :: POST , & url)
218250 . header ( "Content-Type" , content_type) ;
219251 if let Some ( len) = content_length {
220252 req = req. header ( "Content-Length" , len) ;
@@ -225,7 +257,6 @@ impl ApiClient {
225257 // Authorization) still log.
226258 self . send ( req, None )
227259 }
228-
229260}
230261
231262/// Decide what error text to print for a failed response. Pulled out as a pure
@@ -365,11 +396,7 @@ mod tests {
365396 fn format_fail_message_4xx_no_probe_result_falls_through ( ) {
366397 // Caller couldn't load config (None) — still surface the upstream error.
367398 let body = "plain body" ;
368- let msg = format_fail_message (
369- reqwest:: StatusCode :: NOT_FOUND ,
370- body,
371- None ,
372- ) ;
399+ let msg = format_fail_message ( reqwest:: StatusCode :: NOT_FOUND , body, None ) ;
373400 assert ! ( !msg. contains( "API key is invalid" ) ) ;
374401 assert_eq ! ( msg, "plain body" ) ;
375402 }
0 commit comments