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