use super::{
super::{configuration::*, middleware::*},
annotations::*,
context::*,
templates::*,
};
use {
::axum::{http::*, response::Response},
compris::{normal::*, *},
httpdate::*,
kutil::{
http::*,
std::{error::*, immutable::*},
},
std::{io, path::*, result::Result},
tokio::{fs::*, io::*},
};
#[derive(Clone, Copy, Debug)]
pub enum RenderedPageType {
ContentWithEmbeddedAnnotations,
Annotations(Format),
}
#[derive(Clone, Debug)]
pub struct RenderedPage {
pub headers: HeaderMap,
pub annotations: Annotations,
pub content: Option<ByteString>,
}
impl RenderedPage {
pub async fn new_from_response(
identifier: &str,
rendered_page_type: RenderedPageType,
response: Response,
configuration: &RenderConfiguration,
) -> Result<Self, StatusCode> {
let headers = response.headers().clone();
let body = response.into_body();
let (body, _trailers) = body
.read_into_string(configuration.max_content_size.inner.into())
.await
.map_err_internal_server("read body into string")?;
let (annotations, content) = Self::split(identifier, rendered_page_type, &body, configuration);
Ok(Self { headers, annotations, content })
}
pub async fn new_from_file<PathT>(
rendered_page_type: RenderedPageType,
path: PathT,
configuration: &RenderConfiguration,
) -> io::Result<Self>
where
PathT: AsRef<Path>,
{
let path = path.as_ref();
let mut file = File::open(path).await.with_path(path)?;
let mut string = String::default();
file.read_to_string(&mut string).await?;
let (annotations, content) =
Self::split(path.to_string_lossy().as_ref(), rendered_page_type, &string, configuration);
Ok(Self { headers: Default::default(), annotations, content })
}
pub fn context<'own>(
&'own self,
socket: Option<Socket>,
uri_path: ByteString,
original_uri_path: Option<ByteString>,
query: Option<QueryMap>,
last_modified: Option<HttpDate>,
is_json: (bool, bool),
templates: &'own Templates,
configuration: &'own CredenceConfiguration,
) -> RenderContext<'own> {
let mut variables = configuration.render.variables.clone();
for (key, value) in &self.annotations.variables {
variables.insert(key.clone(), value.clone());
}
RenderContext::new(
self,
variables,
socket,
uri_path,
original_uri_path,
query,
last_modified,
is_json,
self.annotations.renderer(&configuration.render).clone(),
templates,
configuration,
)
}
pub fn merged_headers(&self) -> Result<HeaderMap, StatusCode> {
let mut headers = self.headers.clone();
headers.set_string_values(self.annotations.headers.iter()).map_err_internal_server("header value")?;
Ok(headers)
}
pub fn title(&self, configuration: &RenderConfiguration) -> Result<Option<ByteString>, StatusCode> {
Ok(match self.annotations.variables.get("title") {
Some(title) => match title {
Variant::Text(title) => Some(title.inner.clone()),
_ => None,
},
None => {
let renderer = self.annotations.renderer(configuration);
match self.content.as_ref() {
Some(content) => renderer.title_from_content(&content)?,
None => None,
}
}
})
}
pub fn split(
identifier: &str,
rendered_page_type: RenderedPageType,
string: &str,
configuration: &RenderConfiguration,
) -> (Annotations, Option<ByteString>) {
match rendered_page_type {
RenderedPageType::Annotations(format) => {
let annotations = Annotations::parse(identifier, &string, format);
(annotations, None)
}
RenderedPageType::ContentWithEmbeddedAnnotations => {
let (annotations, content) = configuration.annotations.split(identifier, &string);
(annotations, Some(content.into()))
}
}
}
}