mirror of
https://gitlab.com/msrd0/gotham-restful.git
synced 2025-02-23 04:52:28 +00:00
documentation
This commit is contained in:
parent
4429fced3b
commit
853ee3e94c
8 changed files with 274 additions and 35 deletions
|
@ -2,8 +2,21 @@
|
||||||
stages:
|
stages:
|
||||||
- check
|
- check
|
||||||
|
|
||||||
check:
|
check-none:
|
||||||
stage: check
|
stage: check
|
||||||
image: msrd0/rust-pq
|
image: msrd0/rust-pq
|
||||||
script:
|
script:
|
||||||
- cargo check
|
- cargo check --all --no-default-features
|
||||||
|
|
||||||
|
check-all:
|
||||||
|
stage: check
|
||||||
|
image: msrd0/rust-pq
|
||||||
|
script:
|
||||||
|
- cargo check --all --all-features
|
||||||
|
|
||||||
|
readme:
|
||||||
|
stage: check
|
||||||
|
image: msrd0/rust:alpine-readme
|
||||||
|
script:
|
||||||
|
- cargo readme -r gotham_restful -t ../README.tpl >README.md.new
|
||||||
|
- diff README.md README.md.new
|
||||||
|
|
66
README.md
Normal file
66
README.md
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
# gotham_restful [](https://gitlab.com/msrd0/gotham-restful/commits/master)
|
||||||
|
|
||||||
|
This crate is an extension to the popular [gotham web framework][gotham] for Rust. The idea is to
|
||||||
|
have several RESTful resources that can be added to the gotham router. This crate will take care
|
||||||
|
of everything else, like parsing path/query parameters, request bodies, and writing response
|
||||||
|
bodies, relying on [`serde`][serde] and [`serde_json`][serde_json] for (de)serializing. If you
|
||||||
|
enable the `openapi` feature, you can also generate an OpenAPI Specification from your RESTful
|
||||||
|
resources.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
To use this crate, add the following to your `Cargo.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
gotham_restful = "0.0.1"
|
||||||
|
```
|
||||||
|
|
||||||
|
A basic server with only one resource, handling a simple `GET` request, could look like this:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#
|
||||||
|
/// Our RESTful Resource.
|
||||||
|
#[derive(Resource)]
|
||||||
|
#[rest_resource(read_all)]
|
||||||
|
struct UsersResource;
|
||||||
|
|
||||||
|
/// Our return type.
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
struct User {
|
||||||
|
id: i64,
|
||||||
|
username: String,
|
||||||
|
email: String
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Our handler method.
|
||||||
|
#[rest_read_all(UsersResource)]
|
||||||
|
fn read_all(_state: &mut State) -> Success<Vec<User>> {
|
||||||
|
vec![User {
|
||||||
|
id: 1,
|
||||||
|
username: "h4ck3r".to_string(),
|
||||||
|
email: "h4ck3r@example.org".to_string()
|
||||||
|
}].into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Our main method.
|
||||||
|
fn main() {
|
||||||
|
gotham::start("127.0.0.1:8080", build_simple_router(|route| {
|
||||||
|
route.resource::<UsersResource, _>("users");
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Look at the [example] for more methods and usage with the `openapi` feature.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THE ECLIPSE <br/>
|
||||||
|
PUBLIC LICENSE VERSION 2.0. ANY USE, REPRODUCTION OR DISTRIBUTION <br/>
|
||||||
|
OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS LICENSE.
|
||||||
|
|
||||||
|
|
||||||
|
[example]: https://gitlab.com/msrd0/gotham-restful/tree/master/example
|
||||||
|
[gotham]: https://gotham.rs/
|
||||||
|
[serde]: https://github.com/serde-rs/serde#serde-----
|
||||||
|
[serde_json]: https://github.com/serde-rs/json#serde-json----
|
3
README.tpl
Normal file
3
README.tpl
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# {{crate}} {{badges}}
|
||||||
|
|
||||||
|
{{readme}}
|
|
@ -1,8 +1,79 @@
|
||||||
|
/*!
|
||||||
|
This crate is an extension to the popular [gotham web framework][gotham] for Rust. The idea is to
|
||||||
|
have several RESTful resources that can be added to the gotham router. This crate will take care
|
||||||
|
of everything else, like parsing path/query parameters, request bodies, and writing response
|
||||||
|
bodies, relying on [`serde`][serde] and [`serde_json`][serde_json] for (de)serializing. If you
|
||||||
|
enable the `openapi` feature, you can also generate an OpenAPI Specification from your RESTful
|
||||||
|
resources.
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
To use this crate, add the following to your `Cargo.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
gotham_restful = "0.0.1"
|
||||||
|
```
|
||||||
|
|
||||||
|
A basic server with only one resource, handling a simple `GET` request, could look like this:
|
||||||
|
|
||||||
|
```rust,no_run
|
||||||
|
# #[macro_use] extern crate gotham_restful_derive;
|
||||||
|
# use gotham::{router::builder::*, state::State};
|
||||||
|
# use gotham_restful::{DrawResources, Resource, Success};
|
||||||
|
# use serde::{Deserialize, Serialize};
|
||||||
|
#
|
||||||
|
/// Our RESTful Resource.
|
||||||
|
#[derive(Resource)]
|
||||||
|
#[rest_resource(read_all)]
|
||||||
|
struct UsersResource;
|
||||||
|
|
||||||
|
/// Our return type.
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
# #[derive(OpenapiType)]
|
||||||
|
struct User {
|
||||||
|
id: i64,
|
||||||
|
username: String,
|
||||||
|
email: String
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Our handler method.
|
||||||
|
#[rest_read_all(UsersResource)]
|
||||||
|
fn read_all(_state: &mut State) -> Success<Vec<User>> {
|
||||||
|
vec![User {
|
||||||
|
id: 1,
|
||||||
|
username: "h4ck3r".to_string(),
|
||||||
|
email: "h4ck3r@example.org".to_string()
|
||||||
|
}].into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Our main method.
|
||||||
|
fn main() {
|
||||||
|
gotham::start("127.0.0.1:8080", build_simple_router(|route| {
|
||||||
|
route.resource::<UsersResource, _>("users");
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Look at the [example] for more methods and usage with the `openapi` feature.
|
||||||
|
|
||||||
|
# License
|
||||||
|
|
||||||
|
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THE ECLIPSE <br/>
|
||||||
|
PUBLIC LICENSE VERSION 2.0. ANY USE, REPRODUCTION OR DISTRIBUTION <br/>
|
||||||
|
OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS LICENSE.
|
||||||
|
|
||||||
|
|
||||||
|
[example]: https://gitlab.com/msrd0/gotham-restful/tree/master/example
|
||||||
|
[gotham]: https://gotham.rs/
|
||||||
|
[serde]: https://github.com/serde-rs/serde#serde-----
|
||||||
|
[serde_json]: https://github.com/serde-rs/json#serde-json----
|
||||||
|
*/
|
||||||
|
|
||||||
#[macro_use] extern crate gotham_derive;
|
#[macro_use] extern crate gotham_derive;
|
||||||
#[macro_use] extern crate serde;
|
#[macro_use] extern crate serde;
|
||||||
|
|
||||||
pub use hyper::StatusCode;
|
pub use hyper::StatusCode;
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
|
||||||
|
|
||||||
pub use gotham_restful_derive::*;
|
pub use gotham_restful_derive::*;
|
||||||
|
|
||||||
|
@ -17,7 +88,7 @@ pub mod export
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "openapi")]
|
#[cfg(feature = "openapi")]
|
||||||
pub mod openapi;
|
mod openapi;
|
||||||
#[cfg(feature = "openapi")]
|
#[cfg(feature = "openapi")]
|
||||||
pub use openapi::{
|
pub use openapi::{
|
||||||
router::{GetOpenapi, OpenapiRouter},
|
router::{GetOpenapi, OpenapiRouter},
|
||||||
|
@ -49,29 +120,5 @@ pub use routing::{DrawResources, DrawResourceRoutes};
|
||||||
#[cfg(feature = "openapi")]
|
#[cfg(feature = "openapi")]
|
||||||
pub use routing::WithOpenapi;
|
pub use routing::WithOpenapi;
|
||||||
|
|
||||||
|
mod types;
|
||||||
/// A type that can be used inside a request or response body. Implemented for every type
|
pub use types::ResourceType;
|
||||||
/// that is serializable with serde, however, it is recommended to use the rest_struct!
|
|
||||||
/// macro to create one.
|
|
||||||
#[cfg(not(feature = "openapi"))]
|
|
||||||
pub trait ResourceType : DeserializeOwned + Serialize
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "openapi"))]
|
|
||||||
impl<T : DeserializeOwned + Serialize> ResourceType for T
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A type that can be used inside a request or response body. Implemented for every type
|
|
||||||
/// that is serializable with serde, however, it is recommended to use the rest_struct!
|
|
||||||
/// macro to create one.
|
|
||||||
#[cfg(feature = "openapi")]
|
|
||||||
pub trait ResourceType : OpenapiType + DeserializeOwned + Serialize
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "openapi")]
|
|
||||||
impl<T : OpenapiType + DeserializeOwned + Serialize> ResourceType for T
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
|
@ -27,6 +27,12 @@ use openapiv3::{
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use std::panic::RefUnwindSafe;
|
use std::panic::RefUnwindSafe;
|
||||||
|
|
||||||
|
/**
|
||||||
|
This type is required to build routes while adding them to the generated OpenAPI Spec at the
|
||||||
|
same time. There is no need to use this type directly. See [`WithOpenapi`] on how to do this.
|
||||||
|
|
||||||
|
[`WithOpenapi`]: trait.WithOpenapi.html
|
||||||
|
*/
|
||||||
pub struct OpenapiRouter(OpenAPI);
|
pub struct OpenapiRouter(OpenAPI);
|
||||||
|
|
||||||
impl OpenapiRouter
|
impl OpenapiRouter
|
||||||
|
@ -159,6 +165,7 @@ impl Handler for OpenapiHandler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This trait adds the `get_openapi` method to an OpenAPI-aware router.
|
||||||
pub trait GetOpenapi
|
pub trait GetOpenapi
|
||||||
{
|
{
|
||||||
fn get_openapi(&mut self, path : &str);
|
fn get_openapi(&mut self, path : &str);
|
||||||
|
|
|
@ -10,18 +10,32 @@ use openapiv3::{
|
||||||
#[cfg(feature = "chrono")]
|
#[cfg(feature = "chrono")]
|
||||||
use openapiv3::{StringFormat, VariantOrUnknownOrEmpty};
|
use openapiv3::{StringFormat, VariantOrUnknownOrEmpty};
|
||||||
|
|
||||||
|
/**
|
||||||
|
This struct needs to be available for every type that can be part of an OpenAPI Spec. It is
|
||||||
|
already implemented for primitive types, String, Vec, Option and the like. To have it available
|
||||||
|
for your type, simply derive from [`OpenapiType`].
|
||||||
|
|
||||||
|
[`OpenapiType`]: trait.OpenapiType.html
|
||||||
|
*/
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct OpenapiSchema
|
pub struct OpenapiSchema
|
||||||
{
|
{
|
||||||
/// The name of this schema. If it is None, the schema will be inlined.
|
/// The name of this schema. If it is None, the schema will be inlined.
|
||||||
pub name : Option<String>,
|
pub name : Option<String>,
|
||||||
|
/// Whether this particular schema is nullable. Note that there is no guarantee that this will
|
||||||
|
/// make it into the final specification, it might just be interpreted as a hint to make it
|
||||||
|
/// an optional parameter.
|
||||||
pub nullable : bool,
|
pub nullable : bool,
|
||||||
|
/// The actual OpenAPI schema.
|
||||||
pub schema : SchemaKind,
|
pub schema : SchemaKind,
|
||||||
|
/// Other schemas that this schema depends on. They will be included in the final OpenAPI Spec
|
||||||
|
/// along with this schema.
|
||||||
pub dependencies : IndexMap<String, OpenapiSchema>
|
pub dependencies : IndexMap<String, OpenapiSchema>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OpenapiSchema
|
impl OpenapiSchema
|
||||||
{
|
{
|
||||||
|
/// Create a new schema that has no name.
|
||||||
pub fn new(schema : SchemaKind) -> Self
|
pub fn new(schema : SchemaKind) -> Self
|
||||||
{
|
{
|
||||||
Self {
|
Self {
|
||||||
|
@ -31,7 +45,8 @@ impl OpenapiSchema
|
||||||
dependencies: IndexMap::new()
|
dependencies: IndexMap::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert this schema to an `openapiv3::Schema` that can be serialized to the OpenAPI Spec.
|
||||||
pub fn into_schema(self) -> Schema
|
pub fn into_schema(self) -> Schema
|
||||||
{
|
{
|
||||||
Schema {
|
Schema {
|
||||||
|
@ -52,6 +67,20 @@ impl OpenapiSchema
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This trait needs to be implemented by every type that is being used in the OpenAPI Spec. It gives
|
||||||
|
access to the [`OpenapiSchema`] of this type. It is provided for primitive types, String and the
|
||||||
|
like. For use on your own types, there is a derive macro:
|
||||||
|
|
||||||
|
```
|
||||||
|
# #[macro_use] extern crate gotham_restful_derive;
|
||||||
|
#
|
||||||
|
#[derive(OpenapiType)]
|
||||||
|
struct MyResponse {
|
||||||
|
message: String
|
||||||
|
}
|
||||||
|
```
|
||||||
|
*/
|
||||||
pub trait OpenapiType
|
pub trait OpenapiType
|
||||||
{
|
{
|
||||||
fn schema() -> OpenapiSchema;
|
fn schema() -> OpenapiSchema;
|
||||||
|
@ -115,7 +144,7 @@ macro_rules! str_types {
|
||||||
(format = $format:ident, $($str_ty:ty),*) => {$(
|
(format = $format:ident, $($str_ty:ty),*) => {$(
|
||||||
impl OpenapiType for $str_ty
|
impl OpenapiType for $str_ty
|
||||||
{
|
{
|
||||||
fn to_schema() -> OpenapiSchema
|
fn schema() -> OpenapiSchema
|
||||||
{
|
{
|
||||||
OpenapiSchema::new(SchemaKind::Type(Type::String(StringType {
|
OpenapiSchema::new(SchemaKind::Type(Type::String(StringType {
|
||||||
format: VariantOrUnknownOrEmpty::Item(StringFormat::$format),
|
format: VariantOrUnknownOrEmpty::Item(StringFormat::$format),
|
||||||
|
|
|
@ -68,7 +68,31 @@ impl<R : ResourceType, E : Error> ResourceResult for Result<R, E>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This can be returned from a resource when there is no cause of an error.
|
/**
|
||||||
|
This can be returned from a resource when there is no cause of an error. For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
# #[macro_use] extern crate gotham_restful_derive;
|
||||||
|
# use gotham::state::State;
|
||||||
|
# use gotham_restful::Success;
|
||||||
|
# use serde::{Deserialize, Serialize};
|
||||||
|
#
|
||||||
|
# #[derive(Resource)]
|
||||||
|
# struct MyResource;
|
||||||
|
#
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
# #[derive(OpenapiType)]
|
||||||
|
struct MyResponse {
|
||||||
|
message: String
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rest_read_all(MyResource)]
|
||||||
|
fn read_all(_state: &mut State) -> Success<MyResponse> {
|
||||||
|
let res = MyResponse { message: "I'm always happy".to_string() };
|
||||||
|
res.into()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
*/
|
||||||
pub struct Success<T>(T);
|
pub struct Success<T>(T);
|
||||||
|
|
||||||
impl<T> From<T> for Success<T>
|
impl<T> From<T> for Success<T>
|
||||||
|
@ -93,7 +117,24 @@ impl<T : ResourceType> ResourceResult for Success<T>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This can be returned from a resource when there is no content to send.
|
/**
|
||||||
|
This is the return type of a resource that doesn't actually return something. It will result
|
||||||
|
in a _204 No Content_ answer by default. You don't need to use this type directly if using
|
||||||
|
the function attributes:
|
||||||
|
|
||||||
|
```
|
||||||
|
# #[macro_use] extern crate gotham_restful_derive;
|
||||||
|
# use gotham::state::State;
|
||||||
|
#
|
||||||
|
# #[derive(Resource)]
|
||||||
|
# struct MyResource;
|
||||||
|
#
|
||||||
|
#[rest_read_all(MyResource)]
|
||||||
|
fn read_all(_state: &mut State) {
|
||||||
|
// do something
|
||||||
|
}
|
||||||
|
```
|
||||||
|
*/
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct NoContent;
|
pub struct NoContent;
|
||||||
|
|
||||||
|
@ -107,17 +148,20 @@ impl From<()> for NoContent
|
||||||
|
|
||||||
impl ResourceResult for NoContent
|
impl ResourceResult for NoContent
|
||||||
{
|
{
|
||||||
|
/// This will always be a _204 No Content_ together with an empty string.
|
||||||
fn to_json(&self) -> Result<(StatusCode, String), SerdeJsonError>
|
fn to_json(&self) -> Result<(StatusCode, String), SerdeJsonError>
|
||||||
{
|
{
|
||||||
Ok((StatusCode::NO_CONTENT, "".to_string()))
|
Ok((Self::default_status(), "".to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the schema of the `()` type.
|
||||||
#[cfg(feature = "openapi")]
|
#[cfg(feature = "openapi")]
|
||||||
fn schema() -> OpenapiSchema
|
fn schema() -> OpenapiSchema
|
||||||
{
|
{
|
||||||
<()>::schema()
|
<()>::schema()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This will always be a _204 No Content_
|
||||||
#[cfg(feature = "openapi")]
|
#[cfg(feature = "openapi")]
|
||||||
fn default_status() -> StatusCode
|
fn default_status() -> StatusCode
|
||||||
{
|
{
|
||||||
|
|
30
gotham_restful/src/types.rs
Normal file
30
gotham_restful/src/types.rs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#[cfg(feature = "openapi")]
|
||||||
|
use crate::OpenapiType;
|
||||||
|
|
||||||
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
|
|
||||||
|
/// A type that can be used inside a request or response body. Implemented for every type
|
||||||
|
/// that is serializable with serde, however, it is recommended to use the rest_struct!
|
||||||
|
/// macro to create one.
|
||||||
|
#[cfg(not(feature = "openapi"))]
|
||||||
|
pub trait ResourceType : DeserializeOwned + Serialize
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "openapi"))]
|
||||||
|
impl<T : DeserializeOwned + Serialize> ResourceType for T
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A type that can be used inside a request or response body. Implemented for every type
|
||||||
|
/// that is serializable with serde, however, it is recommended to use the rest_struct!
|
||||||
|
/// macro to create one.
|
||||||
|
#[cfg(feature = "openapi")]
|
||||||
|
pub trait ResourceType : OpenapiType + DeserializeOwned + Serialize
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "openapi")]
|
||||||
|
impl<T : OpenapiType + DeserializeOwned + Serialize> ResourceType for T
|
||||||
|
{
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue