Skip to content

Commit a441e58

Browse files
authored
Merge pull request #38 from dev-five-git/impl-description
Impl description, async issue
2 parents 8062464 + 6434d3d commit a441e58

File tree

14 files changed

+1228
-1030
lines changed

14 files changed

+1228
-1030
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"changes":{"crates/vespera/Cargo.toml":"Patch","crates/vespera_macro/Cargo.toml":"Patch","crates/vespera_core/Cargo.toml":"Patch"},"note":"Implement description, async error","date":"2025-12-31T07:14:07.530788400Z"}

.claude/settings.local.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"Bash(cargo test:*)"
5+
]
6+
}
7+
}

README.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,73 @@ pub async fn get_user(id: u32) -> Json<User> {
299299
}
300300
```
301301

302+
### Tags and Description
303+
304+
You can add tags and descriptions to your routes for better OpenAPI documentation organization.
305+
306+
#### Tags
307+
308+
Use the `tags` parameter to group your routes in the OpenAPI documentation:
309+
310+
```rust
311+
#[vespera::route(get, tags = ["users"])]
312+
pub async fn list_users() -> Json<Vec<User>> {
313+
// ...
314+
}
315+
316+
#[vespera::route(post, tags = ["users", "admin"])]
317+
pub async fn create_user(Json(user): Json<User>) -> Json<User> {
318+
// ...
319+
}
320+
```
321+
322+
#### Description
323+
324+
There are two ways to add descriptions to your routes:
325+
326+
**1. Using doc comments (recommended):**
327+
328+
Doc comments (`///`) are automatically extracted and used as the route description in OpenAPI:
329+
330+
```rust
331+
/// Get all users from the database
332+
///
333+
/// Returns a list of all registered users.
334+
#[vespera::route(get)]
335+
pub async fn list_users() -> Json<Vec<User>> {
336+
// ...
337+
}
338+
```
339+
340+
**2. Using the `description` parameter:**
341+
342+
You can also explicitly set the description using the `description` parameter. This takes priority over doc comments:
343+
344+
```rust
345+
/// This doc comment will be ignored
346+
#[vespera::route(get, description = "Custom description for OpenAPI")]
347+
pub async fn list_users() -> Json<Vec<User>> {
348+
// ...
349+
}
350+
```
351+
352+
#### Combined Example
353+
354+
```rust
355+
/// Get user by ID
356+
///
357+
/// Retrieves a specific user by their unique identifier.
358+
#[vespera::route(get, path = "/{id}", tags = ["users"])]
359+
pub async fn get_user(Path(id): Path<u32>) -> Json<User> {
360+
// ...
361+
}
362+
363+
#[vespera::route(post, tags = ["users", "admin"], description = "Create a new user account")]
364+
pub async fn create_user(Json(user): Json<User>) -> Json<User> {
365+
// ...
366+
}
367+
```
368+
302369
### Supported HTTP Methods
303370

304371
- `GET`

SKILL.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,42 @@ pub async fn create_user(Json(user): Json<User>) -> Json<User> {
6767
}
6868
```
6969

70+
### 4. Tags and Description
71+
72+
Add tags to group routes and descriptions for OpenAPI documentation.
73+
74+
**Tags:** Use the `tags` parameter to group routes.
75+
76+
```rust
77+
#[vespera::route(get, tags = ["users"])]
78+
pub async fn list_users() -> Json<Vec<User>> { ... }
79+
80+
#[vespera::route(post, tags = ["users", "admin"])]
81+
pub async fn create_user(Json(user): Json<User>) -> Json<User> { ... }
82+
```
83+
84+
**Description:** Two ways to add descriptions:
85+
86+
1. **Doc comments (recommended):** Automatically extracted from `///` comments.
87+
```rust
88+
/// Get all users from the database
89+
#[vespera::route(get)]
90+
pub async fn list_users() -> Json<Vec<User>> { ... }
91+
```
92+
93+
2. **Explicit `description` parameter:** Takes priority over doc comments.
94+
```rust
95+
#[vespera::route(get, description = "Custom description")]
96+
pub async fn list_users() -> Json<Vec<User>> { ... }
97+
```
98+
99+
**Combined example:**
100+
```rust
101+
/// Get user by ID
102+
#[vespera::route(get, path = "/{id}", tags = ["users"])]
103+
pub async fn get_user(Path(id): Path<u32>) -> Json<User> { ... }
104+
```
105+
70106
### 5. Error Handling
71107
Vespera supports `Result<T, E>` return types. It automatically documents both the success capability (200 OK) and the error responses in the OpenAPI spec.
72108

crates/vespera_macro/src/args.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ pub struct RouteArgs {
33
pub path: Option<syn::LitStr>,
44
pub error_status: Option<syn::ExprArray>,
55
pub tags: Option<syn::ExprArray>,
6+
pub description: Option<syn::LitStr>,
67
}
78

89
impl syn::parse::Parse for RouteArgs {
@@ -11,6 +12,7 @@ impl syn::parse::Parse for RouteArgs {
1112
let mut path: Option<syn::LitStr> = None;
1213
let mut error_status: Option<syn::ExprArray> = None;
1314
let mut tags: Option<syn::ExprArray> = None;
15+
let mut description: Option<syn::LitStr> = None;
1416

1517
// Parse comma-separated list of arguments
1618
while !input.is_empty() {
@@ -39,6 +41,11 @@ impl syn::parse::Parse for RouteArgs {
3941
let array: syn::ExprArray = input.parse()?;
4042
tags = Some(array);
4143
}
44+
"description" => {
45+
input.parse::<syn::Token![=]>()?;
46+
let lit: syn::LitStr = input.parse()?;
47+
description = Some(lit);
48+
}
4249
_ => {
4350
return Err(lookahead.error());
4451
}
@@ -60,6 +67,7 @@ impl syn::parse::Parse for RouteArgs {
6067
path,
6168
error_status,
6269
tags,
70+
description,
6371
})
6472
}
6573
}

crates/vespera_macro/src/collector.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use crate::file_utils::{collect_files, file_to_segments};
44
use crate::metadata::{CollectedMetadata, RouteMetadata};
5-
use crate::route::extract_route_info;
5+
use crate::route::{extract_doc_comment, extract_route_info};
66
use anyhow::{Context, Result};
77
use std::path::Path;
88
use syn::Item;
@@ -61,6 +61,12 @@ pub fn collect_metadata(folder_path: &Path, folder_name: &str) -> Result<Collect
6161
};
6262
let route_path = route_path.replace('_', "-");
6363

64+
// Description priority: route attribute > doc comment
65+
let description = route_info
66+
.description
67+
.clone()
68+
.or_else(|| extract_doc_comment(&fn_item.attrs));
69+
6470
metadata.routes.push(RouteMetadata {
6571
method: route_info.method,
6672
path: route_path,
@@ -70,6 +76,7 @@ pub fn collect_metadata(folder_path: &Path, folder_name: &str) -> Result<Collect
7076
signature: quote::quote!(#fn_item).to_string(),
7177
error_status: route_info.error_status.clone(),
7278
tags: route_info.tags.clone(),
79+
description,
7380
});
7481
}
7582
}

0 commit comments

Comments
 (0)