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}