114 lines
3.6 KiB
Rust
114 lines
3.6 KiB
Rust
use serde::Deserialize;
|
|
use std::path::Path;
|
|
|
|
#[derive(Debug, Deserialize, Clone)]
|
|
pub struct ServerOptions {
|
|
pub stun_server: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Clone)]
|
|
pub struct Config {
|
|
pub server: ServerOptions,
|
|
}
|
|
|
|
impl Config {
|
|
pub fn from_file<P: AsRef<Path>>(p: P) -> Result<Self, Box<dyn std::error::Error>> {
|
|
let s = std::fs::read_to_string(p)?;
|
|
let cfg: Config = serde_json::from_str(&s)?;
|
|
Ok(cfg)
|
|
}
|
|
|
|
pub fn load_default() -> Result<Self, Box<dyn std::error::Error>> {
|
|
Self::from_file("appsettings.json")
|
|
}
|
|
}
|
|
|
|
// WASM runtime loader: fetch `appsettings.json` from the hosting origin
|
|
#[cfg(target_arch = "wasm32")]
|
|
pub async fn load_config_from_server() -> Result<Config, Box<dyn std::error::Error>> {
|
|
// First try HTML-injection fast-path: look for a <script id="app-config"> element
|
|
if let Ok(Some(cfg)) = load_config_from_html().await {
|
|
return Ok(cfg);
|
|
}
|
|
|
|
// Fallback to fetching appsettings.json from the hosting origin
|
|
let resp = gloo_net::http::Request::get("appsettings.json").send().await?;
|
|
let text = resp.text().await?;
|
|
let cfg: Config = serde_json::from_str(&text)?;
|
|
Ok(cfg)
|
|
}
|
|
|
|
// Try to read a JSON config injected into index.html inside a script tag
|
|
#[cfg(target_arch = "wasm32")]
|
|
async fn load_config_from_html() -> Result<Option<Config>, Box<dyn std::error::Error>> {
|
|
use wasm_bindgen::JsCast;
|
|
use web_sys::window;
|
|
|
|
let win = window().ok_or("no window")?;
|
|
let doc = win.document().ok_or("no document")?;
|
|
if let Some(elem) = doc.get_element_by_id("app-config") {
|
|
if let Some(text) = elem.text_content() {
|
|
let cfg: Config = serde_json::from_str(&text)?;
|
|
return Ok(Some(cfg));
|
|
}
|
|
}
|
|
Ok(None)
|
|
}
|
|
|
|
// Synchronous HTML fast-path for WASM: read script#app-config synchronously if present.
|
|
#[cfg(target_arch = "wasm32")]
|
|
pub fn load_config_from_html_sync() -> Option<Config> {
|
|
use wasm_bindgen::JsCast;
|
|
use web_sys::window;
|
|
|
|
let win = window().ok()?;
|
|
let doc = win.document()?;
|
|
let elem = doc.get_element_by_id("app-config")?;
|
|
if let Some(text) = elem.text_content() {
|
|
if let Ok(cfg) = serde_json::from_str::<Config>(&text) {
|
|
return Some(cfg);
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
/// Synchronous loader that prefers the HTML-injection fast-path on WASM, otherwise reads file on native.
|
|
pub fn load_config_sync_or_default() -> Config {
|
|
// WASM: try HTML-injection first
|
|
#[cfg(target_arch = "wasm32")]
|
|
{
|
|
if let Some(cfg) = load_config_from_html_sync() {
|
|
return cfg;
|
|
}
|
|
}
|
|
|
|
// Native: try reading appsettings.json from file
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
{
|
|
if let Ok(cfg) = Config::from_file("appsettings.json") {
|
|
return cfg;
|
|
}
|
|
}
|
|
|
|
// Fallback default
|
|
Config { server: ServerOptions { stun_server: crate::constants::DEFAULT_STUN_SERVER.to_string() } }
|
|
}
|
|
|
|
// Native loader convenience wrapper (blocking-friendly)
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
pub async fn load_config_from_server() -> Result<Config, Box<dyn std::error::Error>> {
|
|
// On native we can just read the file synchronously and return
|
|
Ok(Config::from_file("appsettings.json")?)
|
|
}
|
|
|
|
/// High-level loader with fallbacks: try wasm fetch (or native read), otherwise fallback to defaults.
|
|
pub async fn load_config_or_default() -> Config {
|
|
// Try to load via the async loader
|
|
if let Ok(cfg) = load_config_from_server().await {
|
|
return cfg;
|
|
}
|
|
|
|
// Fallback default
|
|
Config { server: ServerOptions { stun_server: crate::constants::DEFAULT_STUN_SERVER.to_string() } }
|
|
}
|