1use super::{
2 super::{configuration::*, middleware::*},
3 annotations::*,
4 context::*,
5 templates::*,
6};
7
8use {
9 ::axum::{http::*, response::Response},
10 bytestring::*,
11 compris::{normal::*, *},
12 httpdate::*,
13 kutil_http::*,
14 kutil_std::error::*,
15 std::{io, path::*, result::Result},
16 tokio::{fs::*, io::*},
17};
18
19#[derive(Clone, Copy, Debug)]
25pub enum RenderedPageType {
26 ContentWithEmbeddedAnnotations,
28
29 Annotations(Format),
31}
32
33#[derive(Clone, Debug)]
39pub struct RenderedPage {
40 pub headers: HeaderMap,
42
43 pub annotations: Annotations,
45
46 pub content: Option<ByteString>,
48}
49
50impl RenderedPage {
51 pub async fn new_from_response(
53 identifier: &str,
54 rendered_page_type: RenderedPageType,
55 response: Response,
56 configuration: &RenderConfiguration,
57 ) -> Result<Self, StatusCode> {
58 let headers = response.headers().clone();
59 let body = response.into_body();
60
61 let (body, _trailers) = body
62 .read_into_string(configuration.max_content_size.value.into())
63 .await
64 .map_err_internal_server("read body into string")?;
65
66 let (annotations, content) = Self::split(identifier, rendered_page_type, &body, configuration);
67
68 Ok(Self { headers, annotations, content })
69 }
70
71 pub async fn new_from_file<PathT>(
73 rendered_page_type: RenderedPageType,
74 path: PathT,
75 configuration: &RenderConfiguration,
76 ) -> io::Result<Self>
77 where
78 PathT: AsRef<Path>,
79 {
80 let path = path.as_ref();
81 let mut file = File::open(path).await.with_path(path)?;
82 let mut string = String::new();
83 file.read_to_string(&mut string).await?;
84
85 let (annotations, content) =
86 Self::split(path.to_string_lossy().as_ref(), rendered_page_type, &string, configuration);
87
88 Ok(Self { headers: HeaderMap::new(), annotations, content })
89 }
90
91 pub fn context<'own>(
93 &'own self,
94 socket: Option<Socket>,
95 uri_path: ByteString,
96 original_uri_path: Option<ByteString>,
97 last_modified: Option<HttpDate>,
98 is_json: (bool, bool),
99 templates: &'own Templates,
100 configuration: &'own CredenceConfiguration,
101 ) -> RenderContext<'own> {
102 let mut variables = configuration.render.variables.clone();
104 for (key, value) in &self.annotations.variables {
105 variables.insert(key.clone(), value.clone());
106 }
107
108 RenderContext::new(
109 self,
110 variables,
111 socket,
112 uri_path,
113 original_uri_path,
114 last_modified,
115 is_json,
116 self.annotations.renderer(&configuration.render).clone(),
117 templates,
118 configuration,
119 )
120 }
121
122 pub fn merged_headers(&self) -> Result<HeaderMap, StatusCode> {
124 let mut headers = self.headers.clone();
125 headers.set_string_values(self.annotations.headers.iter()).map_err_internal_server("header value")?;
126 Ok(headers)
127 }
128
129 pub fn title(&self, configuration: &RenderConfiguration) -> Result<Option<ByteString>, StatusCode> {
131 Ok(match self.annotations.variables.get("title") {
132 Some(title) => match title {
133 Value::Text(title) => Some(title.value.clone()),
134 _ => None,
135 },
136
137 None => {
138 let renderer = self.annotations.renderer(configuration);
139 match self.content.as_ref() {
140 Some(content) => renderer.title_from_content(&content)?,
141 None => None,
142 }
143 }
144 })
145 }
146
147 pub fn split(
149 identifier: &str,
150 rendered_page_type: RenderedPageType,
151 string: &str,
152 configuration: &RenderConfiguration,
153 ) -> (Annotations, Option<ByteString>) {
154 match rendered_page_type {
155 RenderedPageType::Annotations(format) => {
156 let annotations = Annotations::parse(identifier, &string, format);
157 (annotations, None)
158 }
159
160 RenderedPageType::ContentWithEmbeddedAnnotations => {
161 let (annotations, content) = configuration.annotations.split(identifier, &string);
162 (annotations, Some(content.into()))
163 }
164 }
165 }
166}