1+ use convert_case:: { Case , Casing } ;
12use serde:: Deserialize ;
23use serde_yaml:: { Number , Value } ;
34use std:: {
45 collections:: HashMap ,
5- env:: { current_dir, set_current_dir} ,
66 fs:: { self , File } ,
77 io:: Write ,
88} ;
@@ -42,6 +42,8 @@ struct PathOp {
4242 operation_id : Option < String > ,
4343 description : Option < String > ,
4444 #[ serde( default ) ]
45+ tags : Vec < String > ,
46+ #[ serde( default ) ]
4547 parameters : Vec < Parameter > ,
4648 responses : Option < HashMap < String , Response > > ,
4749}
@@ -113,22 +115,22 @@ struct Property {
113115/// This function panics when the `output/` directory does not exist.
114116fn create_lib_dir ( output_name : & str ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
115117 println ! ( "Starting repackaging into crate..." ) ;
116- let source_files = [ "paths.rs" , "types.rs" ] ;
118+ let source_files = [ "paths.rs" , "types.rs" , "util.rs" ] ;
117119
118- if ! fs:: metadata ( output_name) . is_ok ( ) {
120+ if fs:: metadata ( output_name) . is_err ( ) {
119121 panic ! ( "Fatal: Output directory does not exist!" ) ;
120122 }
121123
122124 std:: env:: set_current_dir ( output_name) ?;
123125
124126 for src_file in & source_files {
125- if ! fs:: metadata ( src_file) . is_ok ( ) {
127+ if fs:: metadata ( src_file) . is_err ( ) {
126128 panic ! ( "Source file {} does not exist!" , src_file) ;
127129 }
128130 }
129131
130132 let src_dir = "src" ;
131- fs:: create_dir_all ( & src_dir) ?;
133+ fs:: create_dir_all ( src_dir) ?;
132134
133135 for src_file in source_files {
134136 let dest_path = format ! ( "{}/{}" , src_dir, src_file) ;
@@ -163,32 +165,72 @@ fn make_comment(input: String, indent: usize) -> String {
163165 . concat ( ) ;
164166}
165167
166- fn make_fn_name_from_path ( input : String ) -> String {
168+ fn make_fn_name_from_path ( input : & str ) -> String {
167169 input. replace ( "/api/" , "" ) . replace ( '/' , "_" )
168170}
169171
170172/// Replaces reserved keywords in an input string for use in Rust.
171- fn fix_keywords ( input : String ) -> String {
173+ fn fix_keywords ( input : & str ) -> String {
172174 input
173175 . replace ( "type" , "typ" )
174176 . replace ( "struct" , "structure" )
175177 . replace ( "fn" , "func" )
176178}
177179
178- fn pathop_to_string ( name : String , input : & PathOp , variant : & str ) -> String {
179- format ! (
180- "{}pub fn {}({}) -> Result<Response, Error> {{ return REQWEST_CLIENT.{}(format!(\" {}\" )).execute().await; }}\n " ,
181- make_comment( input. description. clone( ) . unwrap( ) , 0 ) ,
182- input. operation_id. clone( ) . unwrap_or( make_fn_name_from_path( name. clone( ) ) ) ,
183- input. parameters. iter( ) . enumerate( ) . map( |( s, p) | format!(
180+ fn pathop_to_string ( path : & str , input : & PathOp , method : & str ) -> String {
181+ // Create a new struct for the query parameters.
182+ let fn_struct_params = input
183+ . parameters
184+ // Filter out only the query inputs.
185+ . iter ( )
186+ . filter ( |x| x. input == "query" )
187+ . enumerate ( )
188+ . map ( |( s, p) | {
189+ format ! (
190+ "\t {}: {}{}\n " ,
191+ fix_keywords( & p. name) ,
192+ get_inner_type( p. schema. as_ref( ) . unwrap( ) . clone( ) , false ) ,
193+ if s < & input. parameters. len( ) - 1 {
194+ ","
195+ } else {
196+ ""
197+ }
198+ )
199+ } )
200+ . collect :: < String > ( ) ;
201+ let fn_name = input
202+ . operation_id
203+ . clone ( )
204+ . unwrap_or ( make_fn_name_from_path ( & path) ) ;
205+ let fn_struct_name = fn_name. to_case ( Case :: Pascal ) + "Query" ;
206+ let fn_struct = format ! ( "#[derive(Debug, Serialize, Deserialize)]\n pub struct {fn_struct_name} {{\n {fn_struct_params}}}" ) ;
207+ let comment = make_comment ( input. description . clone ( ) . unwrap ( ) , 0 ) ;
208+ let mut path_args = input
209+ . parameters
210+ // Filter out only the path inputs.
211+ . iter ( )
212+ . filter ( |x| x. input == "path" )
213+ . enumerate ( )
214+ . map ( |( s, p) | {
215+ format ! (
184216 "{}: {}{}" ,
185- fix_keywords( p. name. clone ( ) ) ,
217+ fix_keywords( & p. name) ,
186218 get_inner_type( p. schema. as_ref( ) . unwrap( ) . clone( ) , false ) ,
187- if s == input. parameters. len( ) - 1 { "" } else { ", " }
188- ) ) . collect:: <String >( ) ,
189- variant,
190- name
191- )
219+ if s < & input. parameters. len( ) - 1 {
220+ ","
221+ } else {
222+ ""
223+ }
224+ )
225+ } )
226+ . collect :: < String > ( ) ;
227+ if !path_args. is_empty ( ) {
228+ path_args = ", " . to_owned ( ) + & path_args;
229+ }
230+ return format ! (
231+ include_str!( "templates/path.template" ) ,
232+ fn_struct, comment, fn_name, fn_struct_name, path_args, method, path
233+ ) ;
192234}
193235
194236fn get_inner_type ( items : Value , append_vec : bool ) -> String {
@@ -211,14 +253,13 @@ fn get_inner_type(items: Value, append_vec: bool) -> String {
211253 "number" => "f64" . to_owned ( ) ,
212254 "string" => match items. get ( "format" ) {
213255 Some ( x) => match x. as_str ( ) . unwrap ( ) {
214- "uri" => "Uri" . to_owned ( ) ,
215- "date-time" => "DateTime" . to_owned ( ) ,
256+ "uri" => "Url" . to_owned ( ) ,
216257 _ => "String" . to_owned ( ) ,
217258 } ,
218259 None => "String" . to_owned ( ) ,
219260 } ,
220261 "boolean" => "bool" . to_owned ( ) ,
221- "object" => "Json " . to_owned ( ) ,
262+ "object" => "String " . to_owned ( ) ,
222263 "array" => get_inner_type (
223264 match items. get ( "items" ) {
224265 Some ( z) => z. clone ( ) ,
@@ -229,7 +270,7 @@ fn get_inner_type(items: Value, append_vec: bool) -> String {
229270 _ => panic ! ( "unhandled type!" ) ,
230271 } ,
231272 // We don't know what this is so assume a JSON object.
232- None => "Json " . to_owned ( ) ,
273+ None => "String " . to_owned ( ) ,
233274 } ,
234275 } ;
235276 if append_vec {
@@ -247,16 +288,19 @@ fn if_some<F: FnOnce(&T), T>(this: Option<T>, func: F) {
247288}
248289
249290/// Generates the Rust bindings from a file.
250- pub fn gen ( input_path : impl AsRef < std:: path:: Path > , output_name : String ) {
291+ pub fn gen ( input_path : impl AsRef < std:: path:: Path > , url : String ) {
251292 // Parse the schema.
252293 let input = std:: fs:: read_to_string ( input_path) . unwrap ( ) ;
253294 let yaml: Schema = serde_yaml:: from_str ( & input) . unwrap ( ) ;
254295
255296 // Generate output folder.
256- _ = std:: fs:: create_dir ( & output_name ) ;
297+ _ = std:: fs:: create_dir ( "thanix_client" ) ;
257298
258299 // Create and open the output file for structs.
259- let mut types_file = File :: create ( output_name. clone ( ) + "/types.rs" ) . unwrap ( ) ;
300+ let mut types_file = File :: create ( "thanix_client/types.rs" ) . unwrap ( ) ;
301+ types_file
302+ . write_all ( include_str ! ( "templates/usings.template" ) . as_bytes ( ) )
303+ . unwrap ( ) ;
260304
261305 // For every struct.
262306 for ( name, comp) in & yaml. components . schemas {
@@ -285,8 +329,7 @@ pub fn gen(input_path: impl AsRef<std::path::Path>, output_name: String) {
285329 // "string" can mean either a plain or formatted string or an enum declaration.
286330 "string" => match & prop. format {
287331 Some ( x) => match x. as_str ( ) {
288- "uri" => "Uri" . to_owned ( ) ,
289- "date-time" => "DateTime" . to_owned ( ) ,
332+ "uri" => "Url" . to_owned ( ) ,
290333 _ => "String" . to_owned ( ) ,
291334 } ,
292335 None => "String" . to_owned ( ) ,
@@ -295,7 +338,7 @@ pub fn gen(input_path: impl AsRef<std::path::Path>, output_name: String) {
295338 "number" => "f64" . to_owned ( ) ,
296339 "boolean" => "bool" . to_owned ( ) ,
297340 "array" => get_inner_type ( prop. items . as_ref ( ) . unwrap ( ) . clone ( ) , true ) ,
298- "object" => "Json " . to_owned ( ) ,
341+ "object" => "String " . to_owned ( ) ,
299342 _ => todo ! ( ) ,
300343 } ;
301344
@@ -326,41 +369,44 @@ pub fn gen(input_path: impl AsRef<std::path::Path>, output_name: String) {
326369 }
327370
328371 // Create and open the output file for paths.
329- let mut paths_file = File :: create ( output_name. clone ( ) + "/paths.rs" ) . unwrap ( ) ;
372+ let mut paths_file = File :: create ( "thanix_client/paths.rs" ) . unwrap ( ) ;
373+
374+ paths_file
375+ . write_all ( include_str ! ( "templates/usings.template" ) . as_bytes ( ) )
376+ . unwrap ( ) ;
330377
331378 // For every path.
332379 for ( name, path) in & yaml. paths {
333380 if_some ( path. get . as_ref ( ) , |op| {
334381 paths_file
335- . write_all ( pathop_to_string ( name. clone ( ) , op, "get" ) . as_bytes ( ) )
382+ . write_all ( pathop_to_string ( name, op, "get" ) . as_bytes ( ) )
336383 . unwrap ( )
337384 } ) ;
338385 if_some ( path. put . as_ref ( ) , |op| {
339386 paths_file
340- . write_all ( pathop_to_string ( name. clone ( ) , op, "put" ) . as_bytes ( ) )
387+ . write_all ( pathop_to_string ( name, op, "put" ) . as_bytes ( ) )
341388 . unwrap ( )
342389 } ) ;
343390 if_some ( path. post . as_ref ( ) , |op| {
344391 paths_file
345- . write_all ( pathop_to_string ( name. clone ( ) , op, "post" ) . as_bytes ( ) )
392+ . write_all ( pathop_to_string ( name, op, "post" ) . as_bytes ( ) )
346393 . unwrap ( )
347394 } ) ;
348395 if_some ( path. patch . as_ref ( ) , |op| {
349396 paths_file
350- . write_all ( pathop_to_string ( name. clone ( ) , op, "patch" ) . as_bytes ( ) )
397+ . write_all ( pathop_to_string ( name, op, "patch" ) . as_bytes ( ) )
351398 . unwrap ( )
352399 } ) ;
353400 if_some ( path. delete . as_ref ( ) , |op| {
354401 paths_file
355- . write_all ( pathop_to_string ( name. clone ( ) , op, "delete" ) . as_bytes ( ) )
402+ . write_all ( pathop_to_string ( name, op, "delete" ) . as_bytes ( ) )
356403 . unwrap ( )
357404 } ) ;
358405 }
359-
360- _ = match create_lib_dir ( & output_name) {
361- Ok ( ( ) ) => ( ) ,
362- Err ( e) => {
363- panic ! ( "{}" , e) ;
364- }
365- } ;
406+ fs:: write (
407+ "thanix_client/util.rs" ,
408+ format ! ( include_str!( "templates/util.rs.template" ) , url) . as_bytes ( ) ,
409+ )
410+ . unwrap ( ) ;
411+ create_lib_dir ( "thanix_client" ) . unwrap ( ) ;
366412}
0 commit comments