Skip to content

Commit

Permalink
implement auto-documenting routes
Browse files Browse the repository at this point in the history
  • Loading branch information
ThouCheese committed May 4, 2021
1 parent fe23eae commit 521216c
Show file tree
Hide file tree
Showing 6 changed files with 350 additions and 1 deletion.
4 changes: 4 additions & 0 deletions core/codegen/src/attribute/route/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,9 @@ fn codegen_route(route: Route) -> Result<TokenStream> {
let rank = Optional(route.attr.rank);
let format = Optional(route.attr.format.as_ref());

// Get the doc comment
let docstring = &route.docstring;

Ok(quote! {
#handler_fn

Expand Down Expand Up @@ -353,6 +356,7 @@ fn codegen_route(route: Route) -> Result<TokenStream> {
format: #format,
rank: #rank,
sentinels: #sentinels,
docstring: #docstring,
}
}

Expand Down
6 changes: 5 additions & 1 deletion core/codegen/src/attribute/route/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ pub struct Route {
pub handler: syn::ItemFn,
/// The parsed arguments to the user's function.
pub arguments: Arguments,
/// The doc comment describing this route
pub docstring: String,
}

type ArgumentMap = IndexMap<Name, (syn::Ident, syn::Type)>;
Expand Down Expand Up @@ -209,9 +211,11 @@ impl Route {
})
.collect();

let docstring = String::from_attrs("doc", &handler.attrs)?.join("\n");

diags.head_err_or(Route {
attr, path_params, query_params, data_guard, request_guards,
handler, arguments,
handler, arguments, docstring
})
}
}
Expand Down
285 changes: 285 additions & 0 deletions core/lib/src/doc/has_schema.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
pub enum SchemaKind {
Null,
Map,
List,
String,
Num,
Int,
Bool,
Set,
}

pub struct Schema<T> {
pub description: Option<String>,
pub example: Option<T>,
pub name: String,
pub kind: SchemaKind,

}

pub trait HasSchema: Sized {
fn schema() -> Schema<Self>;
}

// impls for the entire serde data model:

// 14 primitve types
impl HasSchema for i8 {
fn schema() -> Schema<Self> {
Schema {
description: None,
example: Some(1),
name: "signed 8-bits integer".to_string(),
kind: SchemaKind::Int,
}
}
}

impl HasSchema for i16 {
fn schema() -> Schema<Self> {
Schema {
description: None,
example: Some(1),
name: "signed 16-bits integer".to_string(),
kind: SchemaKind::Int,
}
}
}

impl HasSchema for i32 {
fn schema() -> Schema<Self> {
Schema {
description: None,
example: Some(1),
name: "signed 32-bits integer".to_string(),
kind: SchemaKind::Int,
}
}
}

impl HasSchema for i64 {
fn schema() -> Schema<Self> {
Schema {
description: None,
example: Some(1),
name: "signed 64-bits integer".to_string(),
kind: SchemaKind::Int,
}
}
}

impl HasSchema for i128 {
fn schema() -> Schema<Self> {
Schema {
description: None,
example: Some(1),
name: "signed 128-bits integer".to_string(),
kind: SchemaKind::Int,
}
}
}

impl HasSchema for u8 {
fn schema() -> Schema<Self> {
Schema {
description: None,
example: Some(1),
name: "unsigned 8-bits integer".to_string(),
kind: SchemaKind::Int,
}
}
}

impl HasSchema for u16 {
fn schema() -> Schema<Self> {
Schema {
description: None,
example: Some(1),
name: "unsigned 16-bits integer".to_string(),
kind: SchemaKind::Int,
}
}
}

impl HasSchema for u32 {
fn schema() -> Schema<Self> {
Schema {
description: None,
example: Some(1),
name: "unsigned 32-bits integer".to_string(),
kind: SchemaKind::Int,
}
}
}

impl HasSchema for u64 {
fn schema() -> Schema<Self> {
Schema {
description: None,
example: Some(1),
name: "unsigned 64-bits integer".to_string(),
kind: SchemaKind::Int,
}
}
}

impl HasSchema for u128 {
fn schema() -> Schema<Self> {
Schema {
description: None,
example: Some(1),
name: "unsigned 128-bits integer".to_string(),
kind: SchemaKind::Int,
}
}
}

// string
impl<'a> HasSchema for &'a str {
fn schema() -> Schema<Self> {
Schema {
description: None,
example: Some("string"),
name: "signed 8-bits integer".to_string(),
kind: SchemaKind::String,
}
}
}

impl<'a> HasSchema for String {
fn schema() -> Schema<Self> {
Schema {
description: None,
example: Some("string".to_string()),
name: "signed 8-bits integer".to_string(),
kind: SchemaKind::String,
}
}
}

// byte array
impl<'a> HasSchema for &'a [u8] {
fn schema() -> Schema<Self> {
Schema {
description: None,
example: None,
name: "An array of bytes".to_string(),
kind: SchemaKind::List,
}
}
}

// option
impl<T: HasSchema> HasSchema for Option<T> {
fn schema() -> Schema<Self> {
let base_schema = T::schema();
Schema {
description: None,
example: Some(base_schema.example),
name: format!("Optional: {}", base_schema.name),
kind: base_schema.kind,
}
}
}

// unit
impl HasSchema for () {
fn schema() -> Schema<Self> {
Schema {
description: None,
example: Some(()),
name: "Nothing".to_string(),
kind: SchemaKind::Null,
}
}
}

// seq
impl<T: HasSchema, const N: usize> HasSchema for [T; N] {
fn schema() -> Schema<Self> {
let base_schema = T::schema();
Schema {
description: None,
example: None, // making an array example requires that T be Copy...
name: format!("Array of {} {}'s", N, base_schema.name),
kind: SchemaKind::List,
}
}
}

impl<T: HasSchema> HasSchema for Vec<T> {
fn schema() -> Schema<Self> {
let base_schema = T::schema();
Schema {
description: None,
example: None, // making an array example requires that T be Copy...
name: format!("Unsized array of {}'s", base_schema.name),
kind: SchemaKind::List,
}
}
}

impl<T: HasSchema> HasSchema for std::collections::HashSet<T> {
fn schema() -> Schema<Self> {
Schema {
description: None,
example: None, // making an array example requires that T be Copy...
name: format!("Set of {}'s", T::schema().name),
kind: SchemaKind::Set,
}
}
}

// tuple
impl<T1: HasSchema> HasSchema for (T1, ) {
fn schema() -> Schema<Self> {
Schema {
description: None,
example: None, // making an array example requires that T be Copy...
name: format!("Unary tuple of an {}", T1::schema().name),
kind: SchemaKind::Set,
}
}
}

impl<T1: HasSchema, T2: HasSchema> HasSchema for (T1, T2) {
fn schema() -> Schema<Self> {
Schema {
description: None,
example: None, // making an array example requires that T be Copy...
name: format!("Tuple of the form ({}, {})", T1::schema().name, T2::schema().name),
kind: SchemaKind::Set,
}
}
}

// todo: extend with macros

// map
impl<K: HasSchema, V: HasSchema> HasSchema for std::collections::HashMap<K, V> {
fn schema() -> Schema<Self> {
Schema {
description: None,
example: None, // making an array example requires that T be Copy...
name: format!("Map from {} to {}", K::schema().name, V::schema().name),
kind: SchemaKind::Map,
}
}
}



// impl<T: HasSchema> HasSchema for Box<T> {
// fn schema() -> Schema<Self> {
// let base_schema = T::schema();
// Schema {
// description: base_schema.description,
// example: base_schema.example.map(Box::new),
// name: base_schema.name,
// kind: base_schema.kind,
// }
// }
// }



48 changes: 48 additions & 0 deletions core/lib/src/doc/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//! Traits and structs related to automagically generating documentation for your Rocket routes

use std::{collections::HashMap, marker::PhantomData};

use rocket_http::ContentType;

mod has_schema;

#[derive(Default)]
pub struct Docs(HashMap<ContentType, DocContent>);

#[derive(Default)]
pub struct DocContent {
title: Option<String>,
description: Option<String>,
content_type: Option<String>,
}

pub struct Resolve<T: ?Sized>(PhantomData<T>);

pub trait Documented {
fn docs() -> Docs;
}

trait Undocumented {
fn docs() -> Docs {
Docs::default()
}
}

impl<T: ?Sized> Undocumented for T { }

impl<T: Documented + ?Sized> Resolve<T> {
pub const DOCUMENTED: bool = true;

pub fn docs() -> Docs {
T::docs()
}
}

// impl<T: Documented + ?Sized> Documented for Json<T> {
// fn docs() -> Docs {
// Docs {
// content_type: Some("application/json".to_string()),
// ..Self::docs()
// }
// }
// }
1 change: 1 addition & 0 deletions core/lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ pub mod fairing;
pub mod error;
pub mod catcher;
pub mod route;
pub mod doc;

// Reexport of HTTP everything.
pub mod http {
Expand Down

0 comments on commit 521216c

Please sign in to comment.