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