inertia_rust/config.rs
1use crate::{template_resolver::TemplateResolver, InertiaVersion, SsrClient};
2
3/// A configuration struct for initializing Inertia. You can directly fill the struct or use
4/// the builder fluent syntax by calling `InertiaConfig::builder()`, and finally `InertiaConfig::build()`.
5///
6/// Note that, even with builder, most of fields are mandatory and trying to build without filling them
7/// will cause your application to `panic!`
8///
9/// * `url` - A valid [href](https://developer.mozilla.org/en-US/docs/Web/API/Location)
10/// of the current application
11/// * `version` - The current asset version of the application.
12/// See [Asset versioning](https://inertiajs.com/asset-versioning) for more
13/// details.
14/// * `template_resolver` - A valid template resolver. Check [Template Resolvers] chapter for more details.
15/// * `with_ssr` - Whether Server-side Rendering should be enabled or not.
16/// * `custom_ssr_client` - An [`Option<SsrClient>`] with the Inertia Server address.
17/// If `None` is given, `SsrClient::default` will
18/// be used.
19/// * `encrypt_history` - Whether to encrypt or not the session. Refer to [History encryption]
20/// for more details.
21///
22/// [Template Resolvers]: https://kaiofelps.github.io/inertia-rust/advanced/template_resolvers.html
23/// [Flash Messages and Validation Errors]: https://kaiofelps.github.io/inertia-rust/advanced/flash-messages.html
24/// [History encryption]: https://inertiajs.com/history-encryption
25pub struct InertiaConfig<V>
26where
27 V: ToString,
28{
29 pub url: &'static str,
30 pub version: InertiaVersion<V>,
31 pub template_resolver: Box<dyn TemplateResolver + Send + Sync>,
32 pub with_ssr: bool,
33 pub custom_ssr_client: Option<SsrClient>,
34 pub encrypt_history: bool,
35}
36
37impl<V> InertiaConfig<V>
38where
39 V: ToString,
40{
41 /// Instatiates a new InertiaConfigBuilder instance. It must be configured using a fluent syntax.
42 ///
43 /// # Examples
44 /// ```rust
45 /// use inertia_rust::{InertiaVersion, InertiaConfig};
46 /// # use inertia_rust::{ViewData, InertiaError, template_resolvers::TemplateResolver};
47 /// #
48 /// # struct YourTemplateResolver;
49 /// #
50 /// # #[async_trait::async_trait(?Send)]
51 /// # impl TemplateResolver for YourTemplateResolver {
52 /// # async fn resolve_template(
53 /// # &self,
54 /// # view_data: ViewData<'_>,
55 /// # ) -> Result<String, InertiaError> {
56 /// # // import the layout root and render it using your template engine
57 /// # // lets pretend we rendered it, so it ended up being the html output below!
58 /// # Ok("<h1>my rendered page!</h1>".to_string())
59 /// # }
60 /// # }
61 /// #
62 /// let inertia_config = InertiaConfig::builder()
63 /// .set_url("http://localhost:8080")
64 /// .set_version(InertiaVersion::Literal("v1"))
65 /// .set_template_resolver(Box::new(YourTemplateResolver))
66 /// .build();
67 /// ```
68 pub fn builder() -> InertiaConfigBuilder<V> {
69 InertiaConfigBuilder::new()
70 }
71}
72
73pub struct InertiaConfigBuilder<V>
74where
75 V: ToString,
76{
77 pub url: Option<&'static str>,
78 pub version: Option<InertiaVersion<V>>,
79 pub template_resolver: Option<Box<dyn TemplateResolver + Send + Sync>>,
80 pub with_ssr: bool,
81 pub custom_ssr_client: Option<SsrClient>,
82 pub encrypt_history: bool,
83}
84
85impl<V> Default for InertiaConfigBuilder<V>
86where
87 V: ToString,
88{
89 fn default() -> Self {
90 Self::new()
91 }
92}
93
94impl<V> InertiaConfigBuilder<V>
95where
96 V: ToString,
97{
98 /// Instatiates a new InertiaConfigBuilder instance. It must be configured using a fluent syntax.
99 ///
100 /// # Examples
101 /// ```rust
102 /// use inertia_rust::{InertiaConfigBuilder, InertiaVersion};
103 ///
104 /// # use inertia_rust::{template_resolvers::TemplateResolver, ViewData, InertiaError};
105 /// # struct YourTemplateResolver;
106 /// #
107 /// # #[async_trait::async_trait(?Send)]
108 /// # impl TemplateResolver for YourTemplateResolver {
109 /// # async fn resolve_template(
110 /// # &self,
111 /// # view_data: ViewData<'_>,
112 /// # ) -> Result<String, InertiaError> {
113 /// # // import the layout root and render it using your template engine
114 /// # // lets pretend we rendered it, so it ended up being the html output below!
115 /// # Ok("<h1>my rendered page!</h1>".to_string())
116 /// # }
117 /// # }
118 /// #
119 /// let inertia_config = InertiaConfigBuilder::new()
120 /// .set_url("http://localhost:8080")
121 /// .set_version(InertiaVersion::Literal("v1"))
122 /// .set_template_resolver(Box::new(YourTemplateResolver))
123 /// .build();
124 /// ```
125 pub fn new() -> Self {
126 Self {
127 url: None,
128 version: None,
129 template_resolver: None,
130 with_ssr: false,
131 custom_ssr_client: None,
132 encrypt_history: false,
133 }
134 }
135
136 pub fn set_ssr_client(mut self, ssr_client: SsrClient) -> Self {
137 self.custom_ssr_client = Some(ssr_client);
138 self
139 }
140
141 pub fn set_url(mut self, url: &'static str) -> Self {
142 self.url = Some(url);
143 self
144 }
145
146 pub fn set_version(mut self, version: InertiaVersion<V>) -> Self {
147 self.version = Some(version);
148 self
149 }
150
151 pub fn set_template_resolver(
152 mut self,
153 template_resolver: Box<dyn TemplateResolver + Send + Sync>,
154 ) -> Self {
155 self.template_resolver = Some(template_resolver);
156 self
157 }
158
159 pub fn enable_ssr(mut self) -> Self {
160 self.with_ssr = true;
161 self
162 }
163
164 pub fn encrypt_history(mut self) -> Self {
165 self.encrypt_history = true;
166 self
167 }
168
169 /// Compile the current `InertiaConfigBuilder` into a valid `InertiaConfig` struct.
170 ///
171 /// # Panics
172 /// Panics if any of the following fields equal [`None`]:
173 /// * `url`
174 /// * `template_resolver`
175 /// * `version`
176 pub fn build(self) -> InertiaConfig<V> {
177 if self.url.is_none() {
178 panic!(
179 "[InertiaConfigBuilder] 'url' is a mandatory field and InertiaConfigBuilder cannot build without it.");
180 }
181
182 if self.template_resolver.is_none() {
183 panic!(
184 "[InertiaConfigBuilder] 'template_resolver' is a mandatory field and InertiaConfigBuilder cannot build without it.");
185 }
186
187 if self.version.is_none() {
188 panic!(
189 "[InertiaConfigBuilder] 'version' is a mandatory field and InertiaConfigBuilder cannot build without it.");
190 }
191
192 InertiaConfig {
193 url: self.url.unwrap(),
194 template_resolver: self.template_resolver.unwrap(),
195 version: self.version.unwrap(),
196 with_ssr: self.with_ssr,
197 custom_ssr_client: self.custom_ssr_client,
198 encrypt_history: self.encrypt_history,
199 }
200 }
201}
202
203#[cfg(test)]
204mod test {
205 use crate::{template_resolver::TemplateResolver, InertiaError, InertiaVersion, ViewData};
206 use std::panic;
207
208 use super::{InertiaConfig, InertiaConfigBuilder};
209
210 // region: --- Mocks
211
212 #[derive(PartialEq, Eq)]
213 struct MyTemplateResolver;
214
215 #[async_trait::async_trait(?Send)]
216 impl TemplateResolver for MyTemplateResolver {
217 async fn resolve_template(&self, _view_data: ViewData<'_>) -> Result<String, InertiaError> {
218 Ok("".to_string())
219 }
220 }
221
222 // endregion: --- Mocks
223
224 // region: --- Tests
225
226 #[test]
227 fn builder_panics_if_critical_fields_are_unset() {
228 // region: --- builders
229 let build_totally_empty = panic::catch_unwind(move || {
230 InertiaConfigBuilder::<&str>::new().build();
231 });
232
233 let build_without_url = panic::catch_unwind(move || {
234 InertiaConfigBuilder::<&str>::new()
235 .set_template_resolver(Box::new(MyTemplateResolver))
236 .set_version(InertiaVersion::Literal("v1"))
237 .build()
238 });
239
240 let build_without_template_resolver = panic::catch_unwind(move || {
241 InertiaConfigBuilder::<&str>::new()
242 .set_url("foo")
243 .set_version(InertiaVersion::Literal("v1"))
244 .build()
245 });
246
247 let build_without_version = panic::catch_unwind(move || {
248 InertiaConfigBuilder::<&str>::new()
249 .set_url("foo")
250 .set_template_resolver(Box::new(MyTemplateResolver))
251 .build()
252 });
253
254 let build_with_critical_fields_filled = panic::catch_unwind(move || {
255 InertiaConfigBuilder::<&str>::new()
256 .set_url("foo")
257 .set_template_resolver(Box::new(MyTemplateResolver))
258 .set_version(InertiaVersion::Literal("v1"))
259 .build()
260 });
261 // endregion: --- builders
262
263 assert!(build_totally_empty.is_err());
264 assert!(build_without_url.is_err());
265 assert!(build_without_template_resolver.is_err());
266 assert!(build_without_version.is_err());
267 assert!(build_with_critical_fields_filled.is_ok());
268 }
269
270 #[test]
271 fn builder_builds_correctly() {
272 let with_builder = InertiaConfigBuilder::<&str>::new()
273 .set_url("foo")
274 .set_template_resolver(Box::new(MyTemplateResolver))
275 .set_version(InertiaVersion::Literal("v1"))
276 .build();
277
278 let directly_initialized = InertiaConfig {
279 url: "foo",
280 template_resolver: Box::new(MyTemplateResolver),
281 version: InertiaVersion::Literal("v1"),
282 with_ssr: false,
283 custom_ssr_client: None,
284 encrypt_history: false,
285 };
286
287 assert_eq!(&with_builder.url, &directly_initialized.url);
288 assert_eq!(
289 &with_builder.version.resolve(),
290 &directly_initialized.version.resolve()
291 );
292 assert_eq!(&with_builder.with_ssr, &directly_initialized.with_ssr);
293 assert_eq!(
294 &with_builder.custom_ssr_client,
295 &directly_initialized.custom_ssr_client
296 );
297 }
298
299 // endregion: --- Tests
300}