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
193
194
195
196
197
198
mod body;
mod content_directory;
mod content_engine;
mod content_index;
mod content_item;
mod content_registry;
mod handlebars_helpers;
mod mime;
mod route;
mod test_lib;
use crate::bug_message;
use bytes::Bytes;
use content_item::RenderingFailedError;
use futures::Stream;
use serde::Serialize;
use std::collections::HashMap;
use std::io;
use thiserror::Error;
pub use self::mime::{MediaRange, MediaType};
pub use content_directory::ContentDirectory;
pub use content_engine::{
ContentEngine, ContentLoadingError, FilesystemBasedContentEngine, TemplateError,
};
pub use content_index::ContentIndex;
pub use content_item::UnregisteredTemplate;
pub use content_registry::{ContentRepresentations, RegisteredContent};
pub use route::Route;
pub trait ByteStream: Stream<Item = Result<Bytes, StreamError>>
where
Self: Unpin,
{
}
impl<T> ByteStream for T where T: Stream<Item = Result<Bytes, StreamError>> + Unpin {}
pub struct Media<Content: ByteStream> {
pub media_type: MediaType,
pub content: Content,
}
impl<Content: ByteStream> Media<Content> {
fn new(media_type: MediaType, content: Content) -> Self {
Self {
media_type,
content,
}
}
}
#[derive(Error, Debug)]
pub enum RenderError {
#[error(transparent)]
RenderingFailed(RenderingFailedError),
#[error("The requested content cannot be rendered as an acceptable media type.")]
CannotProvideAcceptableMediaType,
#[doc(hidden)]
#[error("{} This should never happen: {}", bug_message!(), .0)]
Bug(String),
}
#[derive(Error, Debug)]
pub enum StreamError {
#[error(
"Process exited with {}{}",
match .exit_code {
Some(code) => format!("code {}", code),
None => String::from("unknown code"),
},
.stderr_contents.as_ref().map(|message| format!(": {}", message)).unwrap_or_default(),
)]
ExecutableExitedWithNonzero {
pid: u32,
exit_code: Option<i32>,
stderr_contents: Option<String>,
},
#[error("Executable output could not be captured")]
ExecutableOutputCouldNotBeCaptured { pid: u32 },
#[error("Input/output error during rendering")]
IOError {
#[from]
source: io::Error,
},
#[error("Stream was cancelled")]
Canceled,
}
pub trait Render {
type Output;
fn render<'engine, 'accept, ServerInfo, Engine, Accept>(
&self,
context: RenderContext<'engine, ServerInfo, Engine>,
acceptable_media_ranges: Accept,
) -> Result<Media<Self::Output>, RenderError>
where
ServerInfo: Clone + Serialize,
Engine: ContentEngine<ServerInfo>,
Accept: IntoIterator<Item = &'accept MediaRange>,
Self::Output: ByteStream;
}
const TARGET_MEDIA_TYPE_PROPERTY_NAME: &str = "target-media-type";
const REQUEST_DATA_PROPERTY_NAME: &str = "request";
const ROUTE_PROPERTY_NAME: &str = "route";
const QUERY_PARAMETERS_PROPERTY_NAME: &str = "query-parameters";
const REQUEST_HEADERS_PROPERTY_NAME: &str = "request-headers";
#[derive(Clone, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct RequestData {
pub route: Option<Route>,
pub query_parameters: HashMap<String, String>,
pub request_headers: HashMap<String, String>,
}
#[derive(Clone, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct RenderData<ServerInfo: Clone + Serialize> {
#[serde(rename = "/")]
pub index: ContentIndex,
pub server_info: ServerInfo,
pub target_media_type: Option<MediaType>,
pub request: RequestData,
pub error_code: Option<u16>,
}
pub struct RenderContext<'engine, ServerInfo, Engine>
where
ServerInfo: Clone + Serialize,
Engine: ContentEngine<ServerInfo>,
{
content_engine: &'engine Engine,
data: RenderData<ServerInfo>,
handlebars_render_context: Option<handlebars::RenderContext<'engine, 'engine>>,
}
impl<'engine, ServerInfo, Engine> RenderContext<'engine, ServerInfo, Engine>
where
ServerInfo: Clone + Serialize,
Engine: ContentEngine<ServerInfo>,
{
pub fn into_error_context(self, error_code: u16) -> Self {
RenderContext {
data: RenderData {
error_code: Some(error_code),
..self.data
},
..self
}
}
pub fn with_handlebars_render_context(
self,
handlebars_render_context: handlebars::RenderContext<'engine, 'engine>,
) -> Self {
RenderContext {
handlebars_render_context: Some(handlebars_render_context),
..self
}
}
}