1use super::*;
2
3pub(crate) fn set_global_state(state: Arc<AppState>) -> Result<(), EuvError> {
13 APP_STATE.set(state).map_err(|_: Arc<AppState>| {
14 EuvError::Message(String::from("Global state already initialized"))
15 })
16}
17
18pub(crate) fn get_global_state() -> Option<Arc<AppState>> {
24 APP_STATE.get().cloned()
25}
26
27async fn resolve_inline_js(config: &HtmlConfig) -> String {
40 if inline_bridge_disabled() {
41 return build_module_fallback_bridge(config.get_import_path());
42 }
43 let pkg_dir: PathBuf = config.get_serving_root().join(PKG_DIR_NAME);
44 let js_name: String = config
45 .get_import_path()
46 .rsplit('/')
47 .next()
48 .unwrap_or("")
49 .to_string();
50 if js_name.is_empty() {
51 return build_module_fallback_bridge(config.get_import_path());
52 }
53 let js_path: PathBuf = pkg_dir.join(&js_name);
54 if !js_path.exists() {
55 return build_module_fallback_bridge(config.get_import_path());
56 }
57 let wasm_url: String = if let Some(stem) = js_name.strip_suffix(".js") {
58 format!("pkg/{stem}_bg.wasm")
59 } else {
60 config.get_import_path().replace(".js", "_bg.wasm")
61 };
62 match build_inline_bridge(&pkg_dir, &js_name, &wasm_url).await {
63 Ok(snippet) => snippet,
64 Err(error) => {
65 log::warn!("Falling back to module import bridge: {error}");
66 build_module_fallback_bridge(config.get_import_path())
67 }
68 }
69}
70
71pub(crate) async fn generate_html(config: &HtmlConfig) -> Result<String, EuvError> {
93 let template_content: String = if let Some(custom_path) = config.try_get_custom_index_html() {
94 let bytes: Vec<u8> =
95 read(custom_path)
96 .await
97 .map_err(|error: io::Error| EuvError::IoPath {
98 message: String::from("Failed to read custom index.html"),
99 path: custom_path.to_path_buf(),
100 error,
101 })?;
102 String::from_utf8(bytes).map_err(|error: FromUtf8Error| EuvError::Utf8 {
103 message: String::from("Custom index.html is not valid UTF-8"),
104 error,
105 })?
106 } else if config.get_is_release() {
107 INDEX_HTML_RELEASE.to_string()
108 } else {
109 INDEX_HTML_DEV.to_string()
110 };
111 let inline_js: String = resolve_inline_js(config).await;
112 let base_href_tag: String = if config.get_is_dev_server() {
113 format!(
114 "<base href=\"/{}/\" />",
115 config
116 .get_serving_root()
117 .file_name()
118 .and_then(|name: &ffi::OsStr| name.to_str())
119 .unwrap_or("")
120 )
121 } else {
122 String::new()
123 };
124 let html: String = template_content
125 .replace(BASE_HREF_PLACEHOLDER, &base_href_tag)
126 .replace(IMPORT_PATH_PLACEHOLDER, config.get_import_path())
127 .replace(RELOAD_ROUTE_PLACEHOLDER, RELOAD_ROUTE)
128 .replace(INLINE_JS_PLACEHOLDER, &inline_js);
129 let index_path: PathBuf = config.get_serving_root().join(INDEX_HTML_FILE_NAME);
130 create_dir_all(config.get_serving_root())
131 .await
132 .map_err(|error: io::Error| EuvError::Io {
133 message: String::from("Failed to create static directory"),
134 error,
135 })?;
136 write(&index_path, &html)
137 .await
138 .map_err(|error: io::Error| EuvError::Io {
139 message: String::from("Failed to write index.html"),
140 error,
141 })?;
142 Ok(html)
143}
144
145pub async fn resolve_www_dir(www_dir: &Path) -> PathBuf {
155 if metadata(www_dir.join(INDEX_HTML_FILE_NAME)).await.is_ok() {
156 return www_dir.to_path_buf();
157 }
158 let parent_name: Option<&str> = www_dir
159 .file_name()
160 .and_then(|file_name_os_str: &ffi::OsStr| file_name_os_str.to_str());
161 if let Some(name) = parent_name {
162 let nested: PathBuf = www_dir.join(name);
163 if metadata(nested.join(INDEX_HTML_FILE_NAME)).await.is_ok() {
164 return nested;
165 }
166 }
167 www_dir.to_path_buf()
168}
169
170pub fn resolve_pkg_dir(args: &ModeArgs) -> PathBuf {
183 resolve_out_dir(args)
184}
185
186pub async fn resolve_file_in_base(base: &Path, path: &str) -> Option<PathBuf> {
200 let file_path: PathBuf = base.join(path);
201 let canonical_path: PathBuf = canonicalize(&file_path).await.ok()?;
202 let base_canonical: PathBuf = canonicalize(base).await.ok()?;
203 canonical_path
204 .starts_with(&base_canonical)
205 .then_some(file_path)
206}