Skip to content

Commit 5aa9749

Browse files
authored
Skip 1st line in path macro description expansion (#881)
Previously, the entire doc comment was used as the description, in addition to the summary using the first line of the doc comment, resulting in duplicated text. This commit changes the path macro doc comment extraction logic to split the first line from the rest of the body, and skip all lines until the first non-whitespace line. This results in the description no longer containing the summary. In degenerate cases, such as a doc comment consisting of new lines, this will invoke a full linear search over all lines in the doc comment. This should be relatively acceptable as most reasonable forms of doc comments shouldn't have more than a couple of newlines between the first line and subsequent body. For testing and verification, I've tested this in a private project using the `patch` override section in `Cargo.toml`, and tests needed to be changed to account for this change.
1 parent 365469f commit 5aa9749

File tree

3 files changed

+24
-10
lines changed

3 files changed

+24
-10
lines changed

utoipa-gen/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -657,12 +657,12 @@ pub fn derive_to_schema(input: TokenStream) -> TokenStream {
657657
/// `#[deprecated = "There is better way to do this"]` the reason would not render in OpenAPI spec.
658658
///
659659
/// Doc comment at decorated function will be used for _`description`_ and _`summary`_ of the path.
660-
/// First line of the doc comment will be used as the _`summary`_ and the whole doc comment will be
660+
/// First line of the doc comment will be used as the _`summary`_ while the remaining lines will be
661661
/// used as _`description`_.
662662
/// ```rust
663663
/// /// This is a summary of the operation
664664
/// ///
665-
/// /// All lines of the doc comment will be included to operation description.
665+
/// /// The rest of the doc comment will be included to operation description.
666666
/// #[utoipa::path(get, path = "/operation")]
667667
/// fn operation() {}
668668
/// ```

utoipa-gen/src/path.rs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -376,14 +376,28 @@ impl<'p> ToTokens for Path<'p> {
376376
}
377377
});
378378

379+
let split_comment = self
380+
.doc_comments
381+
.as_ref()
382+
.and_then(|comments| comments.split_first())
383+
.map(|(summary, description)| {
384+
// Skip all whitespace lines
385+
let start_pos = description
386+
.iter()
387+
.position(|s| !s.chars().all(char::is_whitespace));
388+
389+
let trimmed = start_pos
390+
.and_then(|pos| description.get(pos..))
391+
.unwrap_or(description);
392+
393+
(summary, trimmed)
394+
});
395+
379396
let operation: Operation = Operation {
380397
deprecated: &self.deprecated,
381398
operation_id,
382-
summary: self
383-
.doc_comments
384-
.as_ref()
385-
.and_then(|comments| comments.iter().next()),
386-
description: self.doc_comments.as_ref(),
399+
summary: split_comment.map(|(summary, _)| summary),
400+
description: split_comment.map(|(_, description)| description),
387401
parameters: self.path_attr.params.as_ref(),
388402
request_body: self.path_attr.request_body.as_ref(),
389403
responses: self.path_attr.responses.as_ref(),
@@ -427,7 +441,7 @@ impl<'p> ToTokens for Path<'p> {
427441
struct Operation<'a> {
428442
operation_id: Expr,
429443
summary: Option<&'a String>,
430-
description: Option<&'a Vec<String>>,
444+
description: Option<&'a [String]>,
431445
deprecated: &'a Option<bool>,
432446
parameters: &'a Vec<Parameter<'a>>,
433447
request_body: Option<&'a RequestBody<'a>>,

utoipa-gen/tests/path_derive.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ fn derive_path_with_all_info_success() {
158158
common::assert_json_array_len(operation.pointer("/parameters").unwrap(), 1);
159159
assert_value! {operation=>
160160
"deprecated" = r#"true"#, "Api fn deprecated status"
161-
"description" = r#""This is test operation description\n\nAdditional info in long description""#, "Api fn description"
161+
"description" = r#""Additional info in long description""#, "Api fn description"
162162
"summary" = r#""This is test operation description""#, "Api fn summary"
163163
"operationId" = r#""foo_bar_id""#, "Api fn operation_id"
164164
"tags.[0]" = r#""custom_tag""#, "Api fn tag"
@@ -224,7 +224,7 @@ fn derive_path_with_extra_attributes_without_nested_module() {
224224
common::assert_json_array_len(operation.pointer("/parameters").unwrap(), 2);
225225
assert_value! {operation=>
226226
"deprecated" = r#"null"#, "Api operation deprecated"
227-
"description" = r#""This is test operation\n\nThis is long description for test operation""#, "Api operation description"
227+
"description" = r#""This is long description for test operation""#, "Api operation description"
228228
"operationId" = r#""get_foos_by_id_since""#, "Api operation operation_id"
229229
"summary" = r#""This is test operation""#, "Api operation summary"
230230
"tags.[0]" = r#""crate""#, "Api operation tag"

0 commit comments

Comments
 (0)