@@ -14,6 +14,8 @@ use clap::{Arg, ArgMatches, App};
1414use serde:: de:: DeserializeOwned ;
1515use std:: collections:: HashMap ;
1616use std:: option:: Option :: * ;
17+ use std:: io:: Read ;
18+ use std:: path:: Path ;
1719use url:: { Url } ;
1820
1921fn main ( ) {
@@ -48,6 +50,12 @@ fn main() {
4850 Err ( e) => println ! ( "Error querying projects: {:?}" , e) ,
4951 }
5052
53+ println ! ( ) ;
54+ match client. test_upload ( ) . expect_success ( ) . expect_json :: < ApiAnalysisJobResponse > ( ) {
55+ Ok ( response) => println ! ( "wow a success response: {:?}" , response) ,
56+ Err ( e) => println ! ( "Error uploading file: {:?}" , e) ,
57+ }
58+
5159 println ! ( ) ;
5260 println ! ( "Done." ) ;
5361 } ,
@@ -73,29 +81,28 @@ struct ApiProject {
7381 parent_id : Option < u32 > ,
7482}
7583
84+ #[ derive( Debug , Deserialize ) ]
85+ struct ApiAnalysisJobResponse {
86+ #[ serde( rename = "analysisId" ) ]
87+ analysis_id : u32 ,
88+ #[ serde( rename = "jobId" ) ]
89+ job_id : String
90+ }
91+
7692enum ReqBody {
93+ Form ( reqwest:: multipart:: Form ) ,
7794 Json ( serde_json:: Value ) ,
7895 None ,
79- // TODO: support multipart forms
8096}
8197
8298impl From < serde_json:: Value > for ReqBody {
8399 fn from ( json : serde_json:: Value ) -> ReqBody {
84100 ReqBody :: Json ( json)
85101 }
86102}
87-
88- impl From < ReqBody > for hyper:: Body {
89- fn from ( body : ReqBody ) -> hyper:: Body {
90- match body {
91- ReqBody :: Json ( value) => {
92- let raw_body = serde_json:: to_vec ( & value) . unwrap ( ) ;
93- hyper:: Body :: from ( raw_body)
94- } ,
95- ReqBody :: None => {
96- hyper:: Body :: empty ( )
97- }
98- }
103+ impl From < reqwest:: multipart:: Form > for ReqBody {
104+ fn from ( form : reqwest:: multipart:: Form ) -> ReqBody {
105+ ReqBody :: Form ( form)
99106 }
100107}
101108
@@ -107,7 +114,34 @@ struct ApiClient {
107114#[ derive( Debug ) ]
108115enum ApiError {
109116 Protocol ( reqwest:: Error ) ,
110- NonSuccess ( hyper:: StatusCode )
117+ NonSuccess ( hyper:: StatusCode , ApiErrorMessage ) ,
118+ IO ( std:: io:: Error ) ,
119+ }
120+ impl From < std:: io:: Error > for ApiError {
121+ fn from ( e : std:: io:: Error ) -> ApiError {
122+ ApiError :: IO ( e)
123+ }
124+ }
125+
126+ #[ derive( Debug ) ]
127+ enum ApiErrorMessage {
128+ Nice ( String ) ,
129+ Raw ( String )
130+ }
131+ impl ApiErrorMessage {
132+ fn from_body ( response : & mut reqwest:: Response ) -> Result < ApiErrorMessage , ApiError > {
133+ let mut body = String :: new ( ) ;
134+ response. read_to_string ( & mut body) . map_err ( ApiError :: from) . and_then ( |size|{
135+ serde_json:: from_str :: < ErrorMessageResponse > ( & body)
136+ . map ( |err_body| ApiErrorMessage :: Nice ( err_body. error ) )
137+ . or_else ( |_| Ok ( ApiErrorMessage :: Raw ( body) ) )
138+ } )
139+ }
140+ }
141+
142+ #[ derive( Deserialize ) ]
143+ struct ErrorMessageResponse {
144+ error : String
111145}
112146
113147type ApiResult < T > = Result < T , ApiError > ;
@@ -123,11 +157,13 @@ impl ApiResponse {
123157 }
124158
125159 fn expect_success ( self ) -> ApiResponse {
126- ApiResponse ( self . 0 . and_then ( | response| {
160+ ApiResponse ( self . 0 . and_then ( move | mut response| {
127161 if response. status ( ) . is_success ( ) {
128162 Ok ( response)
129163 } else {
130- Err ( ApiError :: NonSuccess ( response. status ( ) ) )
164+ ApiErrorMessage :: from_body ( & mut response) . and_then ( |response_msg| {
165+ Err ( ApiError :: NonSuccess ( response. status ( ) , response_msg) )
166+ } )
131167 }
132168 } ) )
133169 }
@@ -166,6 +202,20 @@ impl ApiClient {
166202 . expect_json ( )
167203 }
168204
205+ fn test_upload ( & self ) -> ApiResponse {
206+ let file_path = Path :: new ( "D:/CodeDx/data-sets/webgoat-r437/src-r437.zip" ) ;
207+ // let file_path = Path::new("./Cargo.toml");
208+ let form = reqwest:: multipart:: Form :: new ( )
209+ . file ( "file1" , file_path)
210+ . map_err ( |e| ApiError :: IO ( e) ) ;
211+
212+ let foo = form. and_then ( |form| {
213+ self . api_post ( & [ "api" , "projects" , "4" , "analysis" ] , form) . get ( )
214+ } ) ;
215+
216+ ApiResponse :: from ( foo)
217+ }
218+
169219 fn api_get ( & self , path_segments : & [ & str ] ) -> ApiResponse {
170220 self . api_request ( hyper:: Method :: Get , path_segments, ReqBody :: None )
171221 }
@@ -192,6 +242,9 @@ impl ApiClient {
192242 ReqBody :: Json ( ref json) => {
193243 request_builder. json ( json) ;
194244 } ,
245+ ReqBody :: Form ( form) => {
246+ request_builder. multipart ( form) ;
247+ }
195248 ReqBody :: None => ( ) ,
196249 } ;
197250 ApiResponse :: from ( request_builder. send ( ) . map_err ( ApiError :: from) )
0 commit comments