Skip to content

Commit e02258e

Browse files
authored
Merge pull request #17 from dev-five-git/redoc
Add redoc
2 parents 39773c5 + 160e1ab commit e02258e

File tree

3 files changed

+67
-4
lines changed

3 files changed

+67
-4
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"changes":{"crates/vespera_core/Cargo.toml":"Patch","crates/vespera_macro/Cargo.toml":"Patch","crates/vespera/Cargo.toml":"Patch"},"note":"Implement redoc","date":"2025-12-03T02:11:00.705272500Z"}

crates/vespera_macro/src/lib.rs

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ struct AutoRouterInput {
5858
title: Option<LitStr>,
5959
version: Option<LitStr>,
6060
docs_url: Option<LitStr>,
61+
redoc_url: Option<LitStr>,
6162
}
6263

6364
impl Parse for AutoRouterInput {
@@ -67,6 +68,7 @@ impl Parse for AutoRouterInput {
6768
let mut title = None;
6869
let mut version = None;
6970
let mut docs_url = None;
71+
let mut redoc_url = None;
7072

7173
while !input.is_empty() {
7274
let lookahead = input.lookahead1();
@@ -88,6 +90,10 @@ impl Parse for AutoRouterInput {
8890
input.parse::<syn::Token![=]>()?;
8991
docs_url = Some(input.parse()?);
9092
}
93+
"redoc_url" => {
94+
input.parse::<syn::Token![=]>()?;
95+
redoc_url = Some(input.parse()?);
96+
}
9197
"title" => {
9298
input.parse::<syn::Token![=]>()?;
9399
title = Some(input.parse()?);
@@ -146,6 +152,11 @@ impl Parse for AutoRouterInput {
146152
.map(|f| LitStr::new(&f, Span::call_site()))
147153
.ok()
148154
}),
155+
redoc_url: redoc_url.or_else(|| {
156+
std::env::var("VESPERA_REDOC_URL")
157+
.map(|f| LitStr::new(&f, Span::call_site()))
158+
.ok()
159+
}),
149160
})
150161
}
151162
}
@@ -164,6 +175,7 @@ pub fn vespera(input: TokenStream) -> TokenStream {
164175
let title = input.title.map(|t| t.value());
165176
let version = input.version.map(|v| v.value());
166177
let docs_url = input.docs_url.map(|u| u.value());
178+
let redoc_url = input.redoc_url.map(|u| u.value());
167179

168180
let folder_path = find_folder_path(&folder_name);
169181

@@ -192,8 +204,9 @@ pub fn vespera(input: TokenStream) -> TokenStream {
192204
metadata.structs.extend(schemas);
193205

194206
let mut docs_info = None;
207+
let mut redoc_info = None;
195208

196-
if openapi_file_name.is_some() || docs_url.is_some() {
209+
if openapi_file_name.is_some() || docs_url.is_some() || redoc_url.is_some() {
197210
// Generate OpenAPI document using collected metadata
198211

199212
// Serialize to JSON
@@ -214,11 +227,14 @@ pub fn vespera(input: TokenStream) -> TokenStream {
214227
std::fs::write(openapi_file_name, &json_str).unwrap();
215228
}
216229
if let Some(docs_url) = docs_url {
217-
docs_info = Some((docs_url, json_str));
230+
docs_info = Some((docs_url, json_str.clone()));
231+
}
232+
if let Some(redoc_url) = redoc_url {
233+
redoc_info = Some((redoc_url, json_str));
218234
}
219235
}
220236

221-
generate_router_code(&metadata, docs_info).into()
237+
generate_router_code(&metadata, docs_info, redoc_info).into()
222238
}
223239

224240
fn find_folder_path(folder_name: &str) -> std::path::PathBuf {
@@ -235,6 +251,7 @@ fn find_folder_path(folder_name: &str) -> std::path::PathBuf {
235251
fn generate_router_code(
236252
metadata: &CollectedMetadata,
237253
docs_info: Option<(String, String)>,
254+
redoc_info: Option<(String, String)>,
238255
) -> proc_macro2::TokenStream {
239256
let mut router_nests = Vec::new();
240257

@@ -318,6 +335,44 @@ fn generate_router_code(
318335
));
319336
}
320337

338+
if let Some((redoc_url, spec)) = redoc_info {
339+
let method_path = http_method_to_token_stream(HttpMethod::Get);
340+
341+
let html = format!(
342+
r#"
343+
<!DOCTYPE html>
344+
<html lang="en">
345+
<head>
346+
<meta charset="UTF-8">
347+
<title>ReDoc</title>
348+
<meta name="viewport" content="width=device-width, initial-scale=1">
349+
<style>
350+
body {{
351+
margin: 0;
352+
padding: 0;
353+
}}
354+
</style>
355+
<link rel="stylesheet" href="https://unpkg.com/redoc/bundles/redoc.standalone.css" />
356+
</head>
357+
<body>
358+
<div id="redoc-container"></div>
359+
<script src="https://unpkg.com/redoc/bundles/redoc.standalone.js"></script>
360+
<script>
361+
const openapiSpec = {spec_json};
362+
Redoc.init(openapiSpec, {{}}, document.getElementById("redoc-container"));
363+
</script>
364+
</body>
365+
</html>
366+
"#,
367+
spec_json = spec
368+
)
369+
.replace("\n", "");
370+
371+
router_nests.push(quote!(
372+
.route(#redoc_url, #method_path(|| async { vespera::axum::response::Html(#html) }))
373+
));
374+
}
375+
321376
quote! {
322377
vespera::axum::Router::new()
323378
#( #router_nests )*
@@ -348,6 +403,7 @@ mod tests {
348403
let result = generate_router_code(
349404
&collect_metadata(&temp_dir.path(), folder_name).unwrap(),
350405
None,
406+
None,
351407
);
352408
let code = result.to_string();
353409

@@ -504,6 +560,7 @@ pub fn get_users() -> String {
504560
let result = generate_router_code(
505561
&collect_metadata(&temp_dir.path(), folder_name).unwrap(),
506562
None,
563+
None,
507564
);
508565
let code = result.to_string();
509566

@@ -588,6 +645,7 @@ pub fn update_user() -> String {
588645
let result = generate_router_code(
589646
&collect_metadata(&temp_dir.path(), folder_name).unwrap(),
590647
None,
648+
None,
591649
);
592650
let code = result.to_string();
593651

@@ -641,6 +699,7 @@ pub fn create_users() -> String {
641699
let result = generate_router_code(
642700
&collect_metadata(&temp_dir.path(), folder_name).unwrap(),
643701
None,
702+
None,
644703
);
645704
let code = result.to_string();
646705

@@ -686,6 +745,7 @@ pub fn index() -> String {
686745
let result = generate_router_code(
687746
&collect_metadata(&temp_dir.path(), folder_name).unwrap(),
688747
None,
748+
None,
689749
);
690750
let code = result.to_string();
691751

@@ -721,6 +781,7 @@ pub fn get_users() -> String {
721781
let result = generate_router_code(
722782
&collect_metadata(&temp_dir.path(), folder_name).unwrap(),
723783
None,
784+
None,
724785
);
725786
let code = result.to_string();
726787

examples/axum-example/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ pub struct TestStruct {
1919
pub fn create_app() -> axum::Router {
2020
vespera!(
2121
openapi = "examples/axum-example/openapi.json",
22-
docs_url = "/docs"
22+
docs_url = "/docs",
23+
redoc_url = "/redoc"
2324
)
2425
.with_state(Arc::new(AppState {
2526
config: "test".to_string(),

0 commit comments

Comments
 (0)