1use std::borrow::Cow;
2use std::sync::Arc;
3
4use actix_files::Files;
5use actix_web::dev::HttpServiceFactory;
6use path_tree::PathTree;
7use tracing::warn;
8
9use crate::spa_service::SpaService;
10use crate::utils::{find_and_parse_build_manifest, serve_index};
11
12#[derive(Debug, Clone)]
30pub struct Spa {
31 index_file: Cow<'static, str>,
32 static_resources_mount: Cow<'static, str>,
33 static_resources_location: Cow<'static, str>,
34}
35
36impl Spa {
37 pub fn new() -> Self {
39 Self::default()
40 }
41
42 pub fn index_file(mut self, index_file: impl Into<String>) -> Self {
44 self.index_file = Cow::Owned(index_file.into());
45 self
46 }
47
48 pub fn static_resources_mount(mut self, static_resources_mount: impl Into<String>) -> Self {
50 self.static_resources_mount = Cow::Owned(static_resources_mount.into());
51 self
52 }
53
54 pub fn static_resources_location(mut self, static_resources_location: impl Into<String>) -> Self {
56 self.static_resources_location = Cow::Owned(static_resources_location.into());
57 self
58 }
59
60 pub fn finish(self) -> impl HttpServiceFactory {
62 let index_file = Arc::new(self.index_file.into_owned());
63 let static_resources_location = Arc::new(self.static_resources_location.into_owned());
64 let static_resources_mount = self.static_resources_mount.into_owned();
65
66 let path_tree = match find_and_parse_build_manifest(&static_resources_location) {
67 Ok(tree) => Arc::new(tree),
68 Err(e) => {
69 warn!("Failed to parse build manifest: {}. Using default path tree.", e);
70 Arc::new(PathTree::default())
71 }
72 };
73
74 let files = Files::new(&static_resources_mount, static_resources_location.as_str())
75 .index_file("extremely-unlikely-to-exist-!@$%^&*.txt")
76 .default_handler({
77 let index_file = Arc::clone(&index_file);
78 let static_resources_location = Arc::clone(&static_resources_location);
79 let path_tree = Arc::clone(&path_tree);
80
81 move |req| {
82 let index_file = Arc::clone(&index_file);
83 let static_resources_location = Arc::clone(&static_resources_location);
84 let path_tree = Arc::clone(&path_tree);
85
86 async move {
87 serve_index(req, &index_file, &static_resources_location, &path_tree)
88 .await
89 .map_err(actix_web::Error::from)
90 }
91 }
92 });
93
94 SpaService {
95 index_file,
96 static_resources_location,
97 files,
98 path_tree,
99 }
100 }
101}
102
103impl Default for Spa {
105 fn default() -> Self {
106 Self {
107 index_file: Cow::Borrowed("./index.html"),
108 static_resources_mount: Cow::Borrowed("/"),
109 static_resources_location: Cow::Borrowed("./"),
110 }
111 }
112}
113
114pub fn spa() -> Spa {
116 Spa::default()
117}