diff --git a/gotham_restful/Cargo.toml b/gotham_restful/Cargo.toml index 78359fe..13fd54b 100644 --- a/gotham_restful/Cargo.toml +++ b/gotham_restful/Cargo.toml @@ -32,6 +32,7 @@ mime = "0.3.16" openapiv3 = { version = "0.3", optional = true } serde = { version = "1.0.106", features = ["derive"] } serde_json = "1.0.51" +tokio = { version = "0.2.20", optional = true } thiserror = "1.0.15" uuid = { version = ">= 0.1, < 0.9", optional = true } @@ -40,8 +41,9 @@ futures-executor = "0.3.4" paste = "0.1.10" [features] -default = [] +default = ["fs"] auth = ["gotham_restful_derive/auth", "base64", "cookie", "jsonwebtoken"] +fs = ["tokio", "tokio/fs"] errorlog = [] database = ["gotham_restful_derive/database", "gotham_middleware_diesel"] openapi = ["gotham_restful_derive/openapi", "indexmap", "openapiv3"] diff --git a/gotham_restful/src/fs.rs b/gotham_restful/src/fs.rs new file mode 100644 index 0000000..5bd201c --- /dev/null +++ b/gotham_restful/src/fs.rs @@ -0,0 +1,78 @@ +use crate::{ + result::{errorlog, into_response_helper, ResourceError}, + OpenapiSchema, ResourceResult, Response +}; +use futures_util::future::{FutureExt, TryFutureExt}; +use gotham::hyper::StatusCode; +use mime::Mime; +use openapiv3::{SchemaKind, StringFormat, StringType, Type, VariantOrUnknownOrEmpty}; +use std::{ + future::Future, + path::PathBuf, + pin::Pin +}; +use thiserror::Error; + +pub struct File +{ + path : PathBuf, + content_type : Option +} + +impl ResourceResult for File +{ + type Err = tokio::io::Error; + + fn into_response(self) -> Pin> + Send>> + { + async move { + let data = tokio::fs::read(self.path).await?; + let res = Response::new(StatusCode::OK, data, self.content_type); + Ok(res) + }.boxed() + } + + #[cfg(feature = "openapi")] + fn schema() -> OpenapiSchema + { + OpenapiSchema::new(SchemaKind::Type(Type::String(StringType { + format: VariantOrUnknownOrEmpty::Item(StringFormat::Binary), + ..Default::default() + }))) + } +} + +#[derive(Debug, Error)] +pub enum IoOrJsonError +{ + #[error("{0}")] + IoError(#[from] tokio::io::Error), + #[error("{0}")] + JsonError(#[from] serde_json::Error) +} + +impl ResourceResult for Result +{ + type Err = IoOrJsonError; + + fn into_response(self) -> Pin> + Send>> + { + match self { + Ok(f) => f.into_response().map_err(|e| e.into()).boxed(), + Err(e) => into_response_helper(|| { + errorlog(&e); + let err : ResourceError = e.into(); + Ok(Response::json(StatusCode::INTERNAL_SERVER_ERROR, serde_json::to_string(&err)?)) + }) + } + } + + #[cfg(feature = "openapi")] + fn schema() -> OpenapiSchema + { + OpenapiSchema::new(SchemaKind::Type(Type::String(StringType { + format: VariantOrUnknownOrEmpty::Item(StringFormat::Binary), + ..Default::default() + }))) + } +} diff --git a/gotham_restful/src/lib.rs b/gotham_restful/src/lib.rs index c62ce0b..74a4a83 100644 --- a/gotham_restful/src/lib.rs +++ b/gotham_restful/src/lib.rs @@ -152,6 +152,11 @@ pub use auth::{ StaticAuthHandler }; +#[cfg(feature = "fs")] +mod fs; +#[cfg(feature = "fs")] +pub use fs::File; + pub mod matcher; #[cfg(feature = "openapi")] diff --git a/gotham_restful/src/result.rs b/gotham_restful/src/result.rs index 792298f..6407b7e 100644 --- a/gotham_restful/src/result.rs +++ b/gotham_restful/src/result.rs @@ -132,7 +132,7 @@ impl From for ResourceError } } -fn into_response_helper(create_response : F) -> Pin> + Send>> +pub fn into_response_helper(create_response : F) -> Pin> + Send>> where Err : Send + 'static, F : FnOnce() -> Result @@ -142,13 +142,13 @@ where } #[cfg(feature = "errorlog")] -fn errorlog(e : E) +pub fn errorlog(e : E) { error!("The handler encountered an error: {}", e); } #[cfg(not(feature = "errorlog"))] -fn errorlog(_e : E) {} +pub fn errorlog(_e : E) {} impl ResourceResult for Result where @@ -413,13 +413,11 @@ impl, E : Error> ResourceResult for Res { match self { Ok(r) => r.into_response(), - Err(e) => { - into_response_helper(|| { - errorlog(&e); - let err : ResourceError = e.into(); - Ok(Response::json(StatusCode::INTERNAL_SERVER_ERROR, serde_json::to_string(&err)?)) - }) - } + Err(e) => into_response_helper(|| { + errorlog(&e); + let err : ResourceError = e.into(); + Ok(Response::json(StatusCode::INTERNAL_SERVER_ERROR, serde_json::to_string(&err)?)) + }) } } @@ -514,6 +512,7 @@ where match self { Ok(nc) => nc.into_response(), Err(e) => into_response_helper(|| { + errorlog(&e); let err : ResourceError = e.into(); Ok(Response::json(StatusCode::INTERNAL_SERVER_ERROR, serde_json::to_string(&err)?)) }) @@ -603,6 +602,7 @@ where match self { Ok(raw) => raw.into_response(), Err(e) => into_response_helper(|| { + errorlog(&e); let err : ResourceError = e.into(); Ok(Response::json(StatusCode::INTERNAL_SERVER_ERROR, serde_json::to_string(&err)?)) })