lib_humus/
format.rs

1
2use serde::Deserialize;
3use serde::Serialize;
4use mime::Mime;
5
6/// Defines how the response should be rendered.
7#[derive(Debug, Clone, Copy)]
8pub enum HumusFormatFamily {
9
10	/// When rendering the templating engine will be invoked.
11	Template,
12	
13	/// When rendering the [View] is asked to [generate an API response].
14	/// 
15	/// [View]: ./trait.HumusView.html
16	/// [generate an API response]: ./trait.HumusView.html#method.get_api_response
17	API,
18}
19
20/// Provides information on what to render and how to deliver it to the [HumusEngine].
21///
22/// It is an integral part of a [HumusQuerySettings] implementation.
23/// 
24/// It is best implemented on an enum.
25///
26/// Also have a look at the provided methods! They may be sane defaults
27/// for testing and prototyping, implementing them yourself on enum match logic
28/// will improve performance and help with providing missing values.
29///
30/// For a quick prototype implementing ToString and the `from_name()`
31/// method is enough in most cases.
32///
33/// Recommended macros:
34/// ```rust,ignore
35/// #[derive(Clone,Serialize,Deserialize,Default)]
36/// #[serde(rename_all="lowercase")]
37/// ```
38///
39/// For an example implementation see:
40///  [HtmlTextJsonFormat](./enum.HtmlTextJsonFormat.html)
41///
42/// [HumusEngine]: ./struct.HumusEngine.html
43/// [HumusQuerySettings]: ./trait.HumusQuerySettings.html
44pub trait HumusFormat: ToString+Clone+Default+Send {
45
46	/// Return wheter this format should be processed in Template or API mode.
47	fn get_family(&self) -> HumusFormatFamily {
48		match self.get_name().as_str() {
49			"json" => HumusFormatFamily::API,
50			_ => HumusFormatFamily::Template,
51		}
52	}
53
54	/// Returns the file extnsion for the format.
55	///
56	/// Used for deriving the path for the template name.
57	///
58	/// Defaults to `.{self.get_name()}`
59	/// with the exception of the name being `text`
60	/// then it defaults to `.txt`.
61	fn get_file_extension(&self) -> String {
62		match self.get_name().as_str() {
63			"text" => ".txt".to_string(),
64			_ => ".".to_owned()+&self.get_name(),
65		}
66	}
67
68	/// Returns the name of the format,
69	/// by default taken from the ToString implementation.
70	fn get_name(&self) -> String {
71		self.to_string()
72	}
73
74	/// Allows adding extra mimetypes quickly for prototyping
75	///
76	/// Implementing get_mime_type() properly is recommended
77	/// for production use. 
78	fn get_less_well_known_mimetype(&self) -> Option<Mime> {
79		None
80	}
81
82	/// Returns the Mimetype that is expected for this output format.
83	///
84	/// It is recommended to implement this when in production use.
85	///
86	/// For prototyping the default implementation makes assumptions
87	/// based on the output of get_name(), falling back
88	/// to get_less_well_known_mimetype() and the "application/octet-stream" type.
89	///
90	/// The default implementation knows the following associations:
91	/// * `text`: `text/plain; charset=utf-8`
92	/// * `html`: `text/html; charset=utf-8`
93	/// * `json`: `application/json`
94	/// * `xml`: `application/xml`
95	/// * `rss`: `application/rss+xml`
96	/// * `atom`: `application/atom+xml`
97	///
98	/// *Implementation Note:* It may be possible that two different views
99	/// have the same MimeType (maybe two json representations for different consumers).
100	/// 
101	fn get_mime_type(&self) -> Mime {
102		match self.get_name().as_str() {
103			"text" => mime::TEXT_PLAIN_UTF_8,
104			"html" => mime::TEXT_HTML_UTF_8,
105			"json" => mime::APPLICATION_JSON,
106			"xml" => "application/xml".parse()
107				.expect("Parse static application/xml"),
108			"rss" => "application/rss+xml".parse()
109				.expect("Parse static application/rss+xml"),
110			"atom" =>  "application/atom+xml".parse()
111				.expect("Parse static application/atom+xml"),
112			_ =>
113				self.get_less_well_known_mimetype()
114					.unwrap_or(mime::APPLICATION_OCTET_STREAM),
115		}
116	}
117
118	/// Constructs a view from its name.
119	fn from_name(name: &str) -> Option<Self>;
120}
121
122// Some Sample implementations
123
124/// Ready to use example implementation of a [HumusFormat]
125/// featuring `html`, `text` and `json`.
126///
127/// When using this it is recommended to crete a type alias
128/// in case this isn't sufficient in the future.
129/// ```
130/// type MyResponseFormat = lib_humus::HtmlTextJsonFormat;
131/// ```
132#[derive(Debug,Clone,Serialize,Deserialize,Default)]
133#[serde(rename_all="lowercase")]
134pub enum HtmlTextJsonFormat {
135
136	/// An html response.
137	#[default]
138	Html,
139
140	/// A plain text response to be viewed in a terminal.
141	Text,
142
143	/// A json response for other programs.
144	Json,
145}
146
147impl ToString for HtmlTextJsonFormat {
148	fn to_string(&self) -> String {
149		match self {
150			Self::Html => "html",
151			Self::Text => "text",
152			Self::Json => "json",
153		}.to_owned()
154	}
155}
156
157
158impl HumusFormat for HtmlTextJsonFormat {
159
160	fn get_family(&self) -> HumusFormatFamily {
161		match self {
162			Self::Json => HumusFormatFamily::API,
163			_ => HumusFormatFamily::Template,
164		}
165	}
166
167	fn get_file_extension(&self) -> String {
168		match self {
169			Self::Text => ".txt",
170			Self::Html => ".html",
171			Self::Json => ".json",
172		}.to_string()
173	}
174
175	fn get_mime_type(&self) -> Mime {
176		match self {
177			Self::Text => mime::TEXT_PLAIN_UTF_8,
178			Self::Html => mime::TEXT_HTML_UTF_8,
179			Self::Json => mime::APPLICATION_JSON,
180		}
181	}
182
183	fn from_name(name: &str) -> Option<Self> {
184		match name {
185			"html" => Some(Self::Html),
186			"text" => Some(Self::Text),
187			"json" => Some(Self::Json),
188			_ => None,
189		}
190	}
191}
192
193