@@ -21,6 +21,7 @@ use std::fmt::Write;
21
21
use anyhow:: { anyhow, bail} ;
22
22
use clients_schema:: Property ;
23
23
use indexmap:: indexmap;
24
+ use icu_segmenter:: SentenceSegmenter ;
24
25
use openapiv3:: {
25
26
MediaType , Parameter , ParameterData , ParameterSchemaOrContent , PathItem , PathStyle , Paths , QueryStyle , ReferenceOr ,
26
27
RequestBody , Response , Responses , StatusCode ,
@@ -191,11 +192,13 @@ pub fn add_endpoint(
191
192
192
193
parameters. append ( & mut query_params. clone ( ) ) ;
193
194
195
+ let sum_desc = split_summary_desc ( & endpoint. description ) ;
196
+
194
197
// Create the operation, it will be repeated if we have several methods
195
198
let operation = openapiv3:: Operation {
196
199
tags : vec ! [ endpoint. name. clone( ) ] ,
197
- summary : Some ( endpoint . description . clone ( ) ) ,
198
- description : Some ( endpoint . description . clone ( ) ) ,
200
+ summary : sum_desc . summary ,
201
+ description : sum_desc . description ,
199
202
external_docs : tac. convert_external_docs ( endpoint) ,
200
203
operation_id : None , // set in clone_operation below with operation_counter
201
204
parameters,
@@ -311,6 +314,39 @@ fn get_path_parameters(template: &str) -> Vec<&str> {
311
314
result
312
315
}
313
316
317
+ // Splits the original endpoint description into OpenAPI summary and description, where summary
318
+ // is the first sentence of the original description with no trailing `.`, and description contains
319
+ // the remaining sentences, if there are any left.
320
+ fn split_summary_desc ( desc : & str ) -> SplitDesc {
321
+ let segmenter = SentenceSegmenter :: new ( ) ;
322
+
323
+ let desc_no_newlines = desc. replace ( "\n \n " , ".\n " ) . replace ( '\n' , " " ) ;
324
+
325
+ let breakpoints: Vec < usize > = segmenter
326
+ . segment_str ( & desc_no_newlines)
327
+ . collect ( ) ;
328
+
329
+ if breakpoints. len ( ) <2 {
330
+ return SplitDesc {
331
+ summary : None ,
332
+ description : None
333
+ }
334
+ }
335
+ let first_line = & desc_no_newlines[ breakpoints[ 0 ] ..breakpoints[ 1 ] ] ;
336
+ let rest = & desc_no_newlines[ breakpoints[ 1 ] ..breakpoints[ breakpoints. len ( ) -1 ] ] ;
337
+
338
+ SplitDesc {
339
+ summary : Some ( String :: from ( first_line. trim ( ) . strip_suffix ( '.' ) . unwrap_or ( first_line) ) ) ,
340
+ description : if !rest. is_empty ( ) { Some ( String :: from ( rest. trim ( ) ) ) } else { None }
341
+ }
342
+ }
343
+
344
+ #[ derive( PartialEq , Debug ) ]
345
+ struct SplitDesc {
346
+ summary : Option < String > ,
347
+ description : Option < String >
348
+ }
349
+
314
350
#[ cfg( test) ]
315
351
mod tests {
316
352
use super :: * ;
@@ -325,4 +361,33 @@ mod tests {
325
361
assert_eq ! ( get_path_parameters( "{index}{id/" ) , vec! { "index" } ) ;
326
362
assert_eq ! ( get_path_parameters( "{index{id}/" ) , vec! { "index{id" } ) ;
327
363
}
364
+
365
+ #[ test]
366
+ fn test_split_summary_desc ( ) {
367
+ assert_eq ! ( split_summary_desc( "One sentence." ) ,
368
+ SplitDesc {
369
+ summary: Some ( String :: from( "One sentence" ) ) ,
370
+ description: None
371
+ } ) ;
372
+ assert_eq ! ( split_summary_desc( "This is\n still one. sentence: all; together" ) ,
373
+ SplitDesc {
374
+ summary: Some ( String :: from( "This is still one. sentence: all; together" ) ) ,
375
+ description: None
376
+ } ) ;
377
+ assert_eq ! ( split_summary_desc( "These are two totally. Separate sentences!" ) ,
378
+ SplitDesc {
379
+ summary: Some ( String :: from( "These are two totally" ) ) ,
380
+ description: Some ( String :: from( "Separate sentences!" ) )
381
+ } ) ;
382
+ assert_eq ! ( split_summary_desc( "Such a weird way to separate sentences\n \n Right?" ) ,
383
+ SplitDesc {
384
+ summary: Some ( String :: from( "Such a weird way to separate sentences" ) ) ,
385
+ description: Some ( String :: from( "Right?" ) )
386
+ } ) ;
387
+ assert_eq ! ( split_summary_desc( "" ) ,
388
+ SplitDesc {
389
+ summary: None ,
390
+ description: None
391
+ } ) ;
392
+ }
328
393
}
0 commit comments