Skip to content

Commit 6388abd

Browse files
committed
implement auto-documenting routes
1 parent fe23eae commit 6388abd

File tree

5 files changed

+75
-1
lines changed

5 files changed

+75
-1
lines changed

core/codegen/src/attribute/route/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,13 @@ fn codegen_route(route: Route) -> Result<TokenStream> {
319319
let rank = Optional(route.attr.rank);
320320
let format = Optional(route.attr.format.as_ref());
321321

322+
// Get the doc comment
323+
let doc_comment = route
324+
.doc_comment
325+
.as_ref()
326+
.map(|dc| quote! { Some(#dc.to_string()) })
327+
.unwrap_or_else(|| quote! { None });
328+
322329
Ok(quote! {
323330
#handler_fn
324331

@@ -353,6 +360,7 @@ fn codegen_route(route: Route) -> Result<TokenStream> {
353360
format: #format,
354361
rank: #rank,
355362
sentinels: #sentinels,
363+
doc_comment: #doc_comment,
356364
}
357365
}
358366

core/codegen/src/attribute/route/parse.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use devise::{syn, Spanned, SpanWrapped, Result, FromMeta};
22
use devise::ext::{SpanDiagnosticExt, TypeExt};
33
use indexmap::{IndexSet, IndexMap};
4+
use syn::{Lit::Str, Meta::NameValue, MetaNameValue};
45

56
use crate::proc_macro_ext::Diagnostics;
67
use crate::http_codegen::{Method, MediaType};
@@ -29,6 +30,8 @@ pub struct Route {
2930
pub handler: syn::ItemFn,
3031
/// The parsed arguments to the user's function.
3132
pub arguments: Arguments,
33+
/// The doc comment describing this route
34+
pub doc_comment: Option<String>
3235
}
3336

3437
type ArgumentMap = IndexMap<Name, (syn::Ident, syn::Type)>;
@@ -209,9 +212,23 @@ impl Route {
209212
})
210213
.collect();
211214

215+
let doc_comment = handler
216+
.attrs
217+
.iter()
218+
.filter(|a| a.path.is_ident("doc"))
219+
.filter_map(|attr| attr.parse_meta().ok())
220+
.filter_map(|meta| if let NameValue(MetaNameValue { lit: Str(string), .. }) = meta {
221+
Some(string.value())
222+
} else {
223+
None
224+
})
225+
.flat_map(|string| string.split('\n').map(|s| s.trim().to_owned()).collect::<Vec<_>>())
226+
.fold(String::new(), |acc, elem| acc + &elem + "\n");
227+
let doc_comment = (!doc_comment.is_empty()).then(|| doc_comment);
228+
212229
diags.head_err_or(Route {
213230
attr, path_params, query_params, data_guard, request_guards,
214-
handler, arguments,
231+
handler, arguments, doc_comment
215232
})
216233
}
217234
}

core/lib/src/doc/mod.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//! Traits and structs related to automagically generating documentation for your Rocket routes
2+
3+
use std::marker::PhantomData;
4+
5+
#[derive(Default)]
6+
pub struct Docs {
7+
title: Option<String>,
8+
description: Option<String>,
9+
content_type: Option<String>,
10+
}
11+
12+
pub struct Resolve<T: ?Sized>(PhantomData<T>);
13+
14+
pub trait Documented {
15+
fn docs() -> Docs;
16+
}
17+
18+
trait Undocumented {
19+
fn docs() -> Docs {
20+
Docs::default()
21+
}
22+
}
23+
24+
impl<T: ?Sized> Undocumented for T { }
25+
26+
impl<T: Documented + ?Sized> Resolve<T> {
27+
pub const DOCUMENTED: bool = true;
28+
29+
pub fn docs() -> Docs {
30+
T::docs()
31+
}
32+
}
33+
34+
// impl<T: Documented + ?Sized> Documented for Json<T> {
35+
// fn docs() -> Docs {
36+
// Docs {
37+
// content_type: Some("application/json".to_string()),
38+
// ..Self::docs()
39+
// }
40+
// }
41+
// }

core/lib/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ pub mod fairing;
124124
pub mod error;
125125
pub mod catcher;
126126
pub mod route;
127+
pub mod doc;
127128

128129
// Reexport of HTTP everything.
129130
pub mod http {

core/lib/src/route/route.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@ pub struct Route {
190190
pub format: Option<MediaType>,
191191
/// The discovered sentinels.
192192
pub(crate) sentinels: Vec<Sentry>,
193+
/// The doc comment associated with this route.
194+
pub(crate) doc_comment: Option<String>,
193195
}
194196

195197
impl Route {
@@ -253,6 +255,7 @@ impl Route {
253255
sentinels: Vec::new(),
254256
handler: Box::new(handler),
255257
rank, uri, method,
258+
doc_comment: None,
256259
}
257260
}
258261

@@ -345,6 +348,9 @@ pub struct StaticInfo {
345348
/// Route-derived sentinels, if any.
346349
/// This isn't `&'static [SentryInfo]` because `type_name()` isn't `const`.
347350
pub sentinels: Vec<Sentry>,
351+
/// The doc comment associated with this route.
352+
pub doc_comment: Option<String>,
353+
348354
}
349355

350356
#[doc(hidden)]
@@ -361,6 +367,7 @@ impl From<StaticInfo> for Route {
361367
format: info.format,
362368
sentinels: info.sentinels.into_iter().collect(),
363369
uri,
370+
doc_comment: info.doc_comment,
364371
}
365372
}
366373
}

0 commit comments

Comments
 (0)