diff --git a/utoipa-gen/src/lib.rs b/utoipa-gen/src/lib.rs index 23250391..31d07c5e 100644 --- a/utoipa-gen/src/lib.rs +++ b/utoipa-gen/src/lib.rs @@ -657,12 +657,12 @@ pub fn derive_to_schema(input: TokenStream) -> TokenStream { /// `#[deprecated = "There is better way to do this"]` the reason would not render in OpenAPI spec. /// /// Doc comment at decorated function will be used for _`description`_ and _`summary`_ of the path. -/// First line of the doc comment will be used as the _`summary`_ and the whole doc comment will be +/// First line of the doc comment will be used as the _`summary`_ while the remaining lines will be /// used as _`description`_. /// ```rust /// /// This is a summary of the operation /// /// -/// /// All lines of the doc comment will be included to operation description. +/// /// The rest of the doc comment will be included to operation description. /// #[utoipa::path(get, path = "/operation")] /// fn operation() {} /// ``` diff --git a/utoipa-gen/src/path.rs b/utoipa-gen/src/path.rs index 495b3bfd..8df80663 100644 --- a/utoipa-gen/src/path.rs +++ b/utoipa-gen/src/path.rs @@ -376,14 +376,28 @@ impl<'p> ToTokens for Path<'p> { } }); + let split_comment = self + .doc_comments + .as_ref() + .and_then(|comments| comments.split_first()) + .map(|(summary, description)| { + // Skip all whitespace lines + let start_pos = description + .iter() + .position(|s| !s.chars().all(char::is_whitespace)); + + let trimmed = start_pos + .and_then(|pos| description.get(pos..)) + .unwrap_or(description); + + (summary, trimmed) + }); + let operation: Operation = Operation { deprecated: &self.deprecated, operation_id, - summary: self - .doc_comments - .as_ref() - .and_then(|comments| comments.iter().next()), - description: self.doc_comments.as_ref(), + summary: split_comment.map(|(summary, _)| summary), + description: split_comment.map(|(_, description)| description), parameters: self.path_attr.params.as_ref(), request_body: self.path_attr.request_body.as_ref(), responses: self.path_attr.responses.as_ref(), @@ -427,7 +441,7 @@ impl<'p> ToTokens for Path<'p> { struct Operation<'a> { operation_id: Expr, summary: Option<&'a String>, - description: Option<&'a Vec>, + description: Option<&'a [String]>, deprecated: &'a Option, parameters: &'a Vec>, request_body: Option<&'a RequestBody<'a>>, diff --git a/utoipa-gen/tests/path_derive.rs b/utoipa-gen/tests/path_derive.rs index a774b8f7..099d04cb 100644 --- a/utoipa-gen/tests/path_derive.rs +++ b/utoipa-gen/tests/path_derive.rs @@ -158,7 +158,7 @@ fn derive_path_with_all_info_success() { common::assert_json_array_len(operation.pointer("/parameters").unwrap(), 1); assert_value! {operation=> "deprecated" = r#"true"#, "Api fn deprecated status" - "description" = r#""This is test operation description\n\nAdditional info in long description""#, "Api fn description" + "description" = r#""Additional info in long description""#, "Api fn description" "summary" = r#""This is test operation description""#, "Api fn summary" "operationId" = r#""foo_bar_id""#, "Api fn operation_id" "tags.[0]" = r#""custom_tag""#, "Api fn tag" @@ -224,7 +224,7 @@ fn derive_path_with_extra_attributes_without_nested_module() { common::assert_json_array_len(operation.pointer("/parameters").unwrap(), 2); assert_value! {operation=> "deprecated" = r#"null"#, "Api operation deprecated" - "description" = r#""This is test operation\n\nThis is long description for test operation""#, "Api operation description" + "description" = r#""This is long description for test operation""#, "Api operation description" "operationId" = r#""get_foos_by_id_since""#, "Api operation operation_id" "summary" = r#""This is test operation""#, "Api operation summary" "tags.[0]" = r#""crate""#, "Api operation tag"