diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 44fe635..28acceb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,36 +1,38 @@ stages: - - check + - test variables: CARGO_HOME: $CI_PROJECT_DIR/cargo -check-none: - stage: check +test-none: + stage: test image: msrd0/rust:alpine before_script: - cargo -V script: - - cargo check --all --no-default-features + - cargo test --all --no-default-features cache: paths: - cargo/ - target/ -check-all: - stage: check - image: msrd0/rust:alpine +test-all: + stage: test + image: msrd0/rust:alpine-tarpaulin before_script: + - apk add --no-cache bash curl - cargo -V script: - - cargo check --all --all-features + - cargo tarpaulin --all --all-features --exclude-files 'cargo/*' --exclude-files 'example/*' --out Xml + - wget -qO- https://codecov.io/bash | bash -s -- -X gcov cache: paths: - cargo/ - target/ readme: - stage: check + stage: test image: msrd0/rust:alpine-readme script: - cargo readme -r gotham_restful -t ../README.tpl >README.md.new diff --git a/gotham_restful/Cargo.toml b/gotham_restful/Cargo.toml index 21d816b..18f39f0 100644 --- a/gotham_restful/Cargo.toml +++ b/gotham_restful/Cargo.toml @@ -30,6 +30,9 @@ openapiv3 = { version = "0.3", optional = true } serde = { version = "1", features = ["derive"] } serde_json = "1" +[dev-dependencies] +thiserror = "1" + [features] default = [] openapi = ["gotham_restful_derive/openapi", "indexmap", "log", "openapiv3"] diff --git a/gotham_restful/src/result.rs b/gotham_restful/src/result.rs index fdd601b..53011fd 100644 --- a/gotham_restful/src/result.rs +++ b/gotham_restful/src/result.rs @@ -174,7 +174,7 @@ impl ResourceResult for Result fn to_json(&self) -> Result<(StatusCode, String), SerdeJsonError> { Ok(match self { - Ok(_) => (StatusCode::NO_CONTENT, "".to_string()), + Ok(_) => (Self::default_status(), "".to_string()), Err(e) => { let err : ResourceError = e.into(); (StatusCode::INTERNAL_SERVER_ERROR, serde_json::to_string(&err)?) @@ -185,12 +185,75 @@ impl ResourceResult for Result #[cfg(feature = "openapi")] fn schema() -> OpenapiSchema { - <()>::schema() + ::schema() } #[cfg(feature = "openapi")] fn default_status() -> StatusCode { - StatusCode::NO_CONTENT + NoContent::default_status() + } +} + +#[cfg(test)] +mod test +{ + use super::*; + use thiserror::Error; + + #[derive(Debug, Default, Deserialize, Serialize)] + #[cfg_attr(feature = "openapi", derive(OpenapiType))] + struct Msg + { + msg : String + } + + #[derive(Debug, Default, Error)] + #[error("An Error")] + struct MsgError; + + #[test] + fn resource_result_ok() + { + let ok : Result = Ok(Msg::default()); + let (status, json) = ok.to_json().expect("didn't expect error response"); + assert_eq!(status, StatusCode::OK); + assert_eq!(json, r#"{"msg":""}"#); + } + + #[test] + fn resource_result_err() + { + let err : Result = Err(MsgError::default()); + let (status, json) = err.to_json().expect("didn't expect error response"); + assert_eq!(status, StatusCode::INTERNAL_SERVER_ERROR); + assert_eq!(json, format!(r#"{{"error":true,"message":"{}"}}"#, err.unwrap_err())); + } + + #[test] + fn success_always_successfull() + { + let success : Success = Msg::default().into(); + let (status, json) = success.to_json().expect("didn't expect error response"); + assert_eq!(status, StatusCode::OK); + assert_eq!(json, r#"{"msg":""}"#); + } + + #[test] + fn no_content_has_empty_json() + { + let no_content = NoContent::default(); + let (status, json) = no_content.to_json().expect("didn't expect error response"); + assert_eq!(status, StatusCode::NO_CONTENT); + assert_eq!(json, ""); + } + + #[test] + fn no_content_result() + { + let no_content = NoContent::default(); + let res_def = no_content.to_json().expect("didn't expect error response"); + let res_err = Result::::Ok(no_content).to_json().expect("didn't expect error response"); + assert_eq!(res_def, res_err); } } diff --git a/gotham_restful_derive/src/lib.rs b/gotham_restful_derive/src/lib.rs index 3e595de..1992aeb 100644 --- a/gotham_restful_derive/src/lib.rs +++ b/gotham_restful_derive/src/lib.rs @@ -1,6 +1,9 @@ extern crate proc_macro; use proc_macro::TokenStream; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use std::env; mod method; use method::{expand_method, Method}; @@ -9,6 +12,18 @@ use resource::expand_resource; #[cfg(feature = "openapi")] mod openapi_type; +fn krate() -> TokenStream2 +{ + if env::var("CARGO_PKG_NAME").unwrap() == "gotham_restful" + { + quote!(crate) + } + else + { + quote!(::gotham_restful) + } +} + #[cfg(feature = "openapi")] #[proc_macro_derive(OpenapiType)] pub fn derive_openapi_type(tokens : TokenStream) -> TokenStream diff --git a/gotham_restful_derive/src/method.rs b/gotham_restful_derive/src/method.rs index 77e9d4f..0e44461 100644 --- a/gotham_restful_derive/src/method.rs +++ b/gotham_restful_derive/src/method.rs @@ -84,11 +84,12 @@ impl Method pub fn expand_method(method : Method, attrs : TokenStream, item : TokenStream) -> TokenStream { + let krate = super::krate(); let ident = parse_macro_input!(attrs as Ident); let fun = parse_macro_input!(item as ItemFn); let (ret, is_no_content) = match fun.sig.output { - ReturnType::Default => (quote!(::gotham_restful::NoContent), true), + ReturnType::Default => (quote!(#krate::NoContent), true), ReturnType::Type(_, ty) => (quote!(#ty), false) }; let args : Vec<(TokenStream2, TokenStream2)> = fun.sig.inputs.iter().map(|arg| match arg { @@ -103,15 +104,15 @@ pub fn expand_method(method : Method, attrs : TokenStream, item : TokenStream) - generics.push(quote!(#ret)); let args : Vec = args.into_iter().map(|(pat, ty)| quote!(#pat : #ty)).collect(); let block = fun.block.stmts; - let ret_stmt = if is_no_content { Some(quote!(#ret::default())) } else { None }; + let ret_stmt = if is_no_content { Some(quote!(Default::default())) } else { None }; let trait_ident = method.trait_ident(); let fn_ident = method.fn_ident(); let setup_ident = method.setup_ident(); let output = quote! { - impl ::gotham_restful::#trait_ident<#(#generics),*> for #ident - where #ident : ::gotham_restful::Resource + impl #krate::#trait_ident<#(#generics),*> for #ident + where #ident : #krate::Resource { fn #fn_ident(#(#args),*) -> #ret { @@ -121,7 +122,7 @@ pub fn expand_method(method : Method, attrs : TokenStream, item : TokenStream) - } #[deny(dead_code)] - fn #setup_ident(route : &mut D) + fn #setup_ident(route : &mut D) { route.#fn_ident::<#ident, #(#generics),*>(); } diff --git a/gotham_restful_derive/src/openapi_type.rs b/gotham_restful_derive/src/openapi_type.rs index 97ed9b4..c063f7e 100644 --- a/gotham_restful_derive/src/openapi_type.rs +++ b/gotham_restful_derive/src/openapi_type.rs @@ -114,6 +114,7 @@ fn expand_field(field : &Field) -> TokenStream2 pub fn expand_struct(input : ItemStruct) -> TokenStream2 { + let krate = super::krate(); let ident = input.ident; let generics = input.generics; @@ -126,11 +127,11 @@ pub fn expand_struct(input : ItemStruct) -> TokenStream2 }; quote!{ - impl #generics ::gotham_restful::OpenapiType for #ident #generics + impl #generics #krate::OpenapiType for #ident #generics { - fn schema() -> ::gotham_restful::OpenapiSchema + fn schema() -> #krate::OpenapiSchema { - use ::gotham_restful::{export::{openapi::*, IndexMap}, OpenapiSchema}; + use #krate::{export::{openapi::*, IndexMap}, OpenapiSchema}; let mut properties : IndexMap>> = IndexMap::new(); let mut required : Vec = Vec::new(); diff --git a/gotham_restful_derive/src/resource.rs b/gotham_restful_derive/src/resource.rs index a2ce5e2..543c9f8 100644 --- a/gotham_restful_derive/src/resource.rs +++ b/gotham_restful_derive/src/resource.rs @@ -28,6 +28,7 @@ impl Parse for MethodList pub fn expand_resource(tokens : TokenStream) -> TokenStream { + let krate = super::krate(); let input = parse_macro_input!(tokens as ItemStruct); let ident = input.ident; @@ -43,14 +44,14 @@ pub fn expand_resource(tokens : TokenStream) -> TokenStream }).collect(); let output = quote! { - impl ::gotham_restful::Resource for #ident + impl #krate::Resource for #ident { fn name() -> String { stringify!(#ident).to_string() } - fn setup(mut route : D) + fn setup(mut route : D) { #(#methods)* }