essence/
lib.rs

1//! # Basic Usage
2//!
3//! ## Defining your Essence package
4//! Essence packages have 3 options for structure variations
5//!
6//! 1. Payload struct
7//! 2. Payload metadata struct
8//! 3. Error metadata struct
9//!
10//! Example using a generic payload struct with no metadata structs
11//! ```
12//! use essence::{ EssenceResponse };
13//! pub type MyResponse<T> = EssenceResponse<T, (), ()>;
14//! ```
15
16mod utils;
17use thiserror::Error;
18
19use serde::{Serialize, Deserialize};
20
21use utils::{ type_of, struct_name };
22
23
24/// The standard shell of all Essence packages
25#[derive(Debug, Serialize, Deserialize)]
26pub struct EssencePackage<T, M> {
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub metadata: Option<M>,
29    pub payload: T,
30}
31
32/// The payload definition of Essence failure responses
33#[derive(Debug, Serialize, Deserialize)]
34pub struct ErrorPayload {
35    pub kind: String,
36    pub error: String,
37    pub message: String,
38    pub stack: Vec<String>,
39}
40
41impl<T> From<&T> for ErrorPayload
42where
43    T: std::error::Error
44{
45    fn from(error: &T) -> Self {
46	let kind = type_of(&error);
47	let name = struct_name(&error);
48
49	ErrorPayload {
50	    kind: kind,
51	    error: name,
52	    message: format!("{}", error),
53	    stack: vec![],
54	}
55    }
56}
57
58
59/// The possible errors that could be raised by this crate
60#[derive(Debug, Error)]
61pub enum EssenceError {
62    #[error("[{0}::{1}( {2} )]")]
63    ErrorPayload(String, String, String),
64}
65
66/// This defines the struct of an Essence Error package
67pub type EssenceErrorPackage<M> = EssencePackage<ErrorPayload, M>;
68
69
70/// Defines the 2 possible package types (success or failure)
71#[derive(Debug, Serialize, Deserialize)]
72#[serde(tag = "type")]
73#[serde(rename_all = "lowercase")]
74pub enum EssenceResponse<P, PM, EM> {
75    Success(EssencePackage<P, PM>),
76    Failure(EssenceErrorPackage<EM>),
77}
78
79
80impl<P, PM, EM> EssenceResponse<P, PM, EM> {
81    pub fn new<E>(payload: Result<P, E>, success_metadata: Option<PM>, error_metadata: Option<EM>, ) -> Self
82    where
83	E: std::error::Error {
84	match payload {
85	    Ok(data) => EssenceResponse::Success(EssencePackage {
86		metadata: success_metadata,
87		payload: data,
88	    }),
89	    Err(error) => EssenceResponse::Failure(EssencePackage {
90		metadata: error_metadata,
91		payload: ErrorPayload::from( &error ),
92	    }),
93	}
94    }
95
96    pub fn success(payload: P, metadata: Option<PM>) -> Self {
97	EssenceResponse::Success(EssencePackage {
98	    metadata: metadata,
99	    payload: payload,
100	})
101    }
102
103    pub fn failure(error: ErrorPayload, metadata: Option<EM>) -> Self {
104	EssenceResponse::Failure(EssencePackage {
105	    metadata: metadata,
106	    payload: error,
107	})
108    }
109
110    pub fn as_result(self) -> Result<P, EssenceError> {
111	match self {
112	    EssenceResponse::Success(pack) => Ok(pack.payload),
113	    EssenceResponse::Failure(pack) => Err(EssenceError::ErrorPayload(pack.payload.kind.clone(), pack.payload.error.clone(), pack.payload.message.clone())),
114	}
115    }
116}
117
118
119#[cfg(test)]
120pub mod tests {
121    use super::*;
122
123    use serde_json::json;
124    use std::fmt;
125    use thiserror::Error;
126
127    #[test]
128    ///
129    fn type_of_test() {
130	let vector = vec![1,2,3,4];
131
132	assert_eq!(
133	    type_of(&vector),
134	    String::from("Vec<i32>") );
135    }
136
137    #[test]
138    ///
139    fn struct_name_test() {
140	let vector = vec![1,2,3,4];
141
142	assert_eq!(
143	    struct_name(&vector),
144	    String::from("Error") );
145    }
146
147    #[test]
148    ///
149    fn enum_test() {
150	#[derive(Debug, Error)]
151	enum AppError<'a> {
152	    #[error("This is so bad input: {0}")]
153	    BadInput(&'a str),
154	}
155
156	let error = AppError::BadInput("This is so bad...");
157	let payload = ErrorPayload::from( &error );
158
159	assert_eq!(
160	    serde_json::to_string_pretty( &json!(payload) ).unwrap(),
161	    String::from(r#"{
162  "error": "BadInput",
163  "kind": "AppError",
164  "message": "This is so bad input: This is so bad...",
165  "stack": []
166}"#));
167	println!("{:?}", error );
168    }
169
170    #[test]
171    ///
172    fn struct_test() {
173	#[derive(Debug, Error)]
174	struct MyError {
175	    msg: String,
176	}
177
178	impl fmt::Display for MyError {
179	    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180		write!(f, "{}", self.msg)
181	    }
182	}
183
184	let error = MyError { msg: "EntryHash(uhCEkNBaVvGRYmJUqsGNrfO8jC9Ij-t77QcmnAk3E3B8qh6TU09QN)".into() };
185	let payload = ErrorPayload::from( &error );
186
187	assert_eq!(
188	    serde_json::to_string_pretty( &json!(payload) ).unwrap(),
189	    String::from(r#"{
190  "error": "MyError",
191  "kind": "MyError",
192  "message": "EntryHash(uhCEkNBaVvGRYmJUqsGNrfO8jC9Ij-t77QcmnAk3E3B8qh6TU09QN)",
193  "stack": []
194}"#));
195	println!("{:?}", error );
196    }
197
198    #[test]
199    ///
200    fn tuple_test() {
201	#[derive(Debug, Error)]
202	struct MyError<'a>(&'a str, String);
203
204	impl<'a> fmt::Display for MyError<'a> {
205	    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206		write!(f, "{}: {}", self.0, self.1 )
207	    }
208	}
209
210	let error = MyError( "BadInput", "This is so bad...".into() );
211	let payload = ErrorPayload::from( &error );
212
213	assert_eq!(
214	    serde_json::to_string_pretty( &json!(payload) ).unwrap(),
215	    String::from(r#"{
216  "error": "MyError",
217  "kind": "MyError",
218  "message": "BadInput: This is so bad...",
219  "stack": []
220}"#));
221	println!("{:?}", error );
222    }
223}