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