deft 0.15.0

Cross platform ui framework
use anyhow::anyhow;
use quick_js::loader::{FsJsModuleLoader, JsModuleLoader};
use std::io::{Error, ErrorKind};

pub struct RemoteModuleLoader {
    base: Option<String>,
}

impl RemoteModuleLoader {
    pub fn new(base: Option<String>) -> Self {
        Self { base }
    }
}

impl JsModuleLoader for RemoteModuleLoader {
    fn load(&mut self, module_name: &str) -> Result<String, Error> {
        let url = if module_name.starts_with("http://") || module_name.starts_with("https://") {
            module_name.to_string()
        } else if let Some(base) = &self.base {
            format!(
                "{}/{}",
                base.trim_end_matches("/"),
                module_name.trim_start_matches("/")
            )
        } else {
            return Err(Error::new(
                ErrorKind::AddrNotAvailable,
                anyhow!("Failed to resolve module: {}", module_name),
            ));
        };
        let body = reqwest::blocking::get(&url)
            .map_err(|e| Error::new(ErrorKind::Other, format!("Failed to request {:?}", e)))?
            .text()
            .map_err(|e| {
                Error::new(
                    ErrorKind::Other,
                    format!("Failed to read response text: {:?}", e),
                )
            })?;
        Ok(body)
    }
}

pub struct DevModuleLoader {
    is_first_load: bool,
    remote_module_loader: RemoteModuleLoader,
}

impl DevModuleLoader {
    pub fn new(base: Option<&str>) -> Self {
        let base = base.map(String::from);
        Self {
            is_first_load: true,
            remote_module_loader: RemoteModuleLoader::new(base),
        }
    }
}

impl JsModuleLoader for DevModuleLoader {
    fn load(&mut self, module_name: &str) -> Result<String, Error> {
        let is_first_load = self.is_first_load;
        self.is_first_load = false;
        let start_time = std::time::Instant::now();
        loop {
            let result = self.remote_module_loader.load(module_name);
            match result {
                Ok(source) => {
                    return Ok(source);
                }
                Err(err) => {
                    if is_first_load || start_time.elapsed() >= std::time::Duration::from_secs(60) {
                        return Err(err);
                    }
                    std::thread::sleep(std::time::Duration::from_millis(1000));
                }
            }
        }
    }
}

pub struct DefaultModuleLoader {
    remote_module_loader: Option<RemoteModuleLoader>,
    fs_module_loader: Option<FsJsModuleLoader>,
}

impl DefaultModuleLoader {
    pub fn new(allow_remote: bool) -> Self {
        let remote_module_loader = if allow_remote {
            Some(RemoteModuleLoader::new(None))
        } else {
            None
        };
        Self {
            remote_module_loader,
            fs_module_loader: None,
        }
    }

    pub fn set_fs_base(&mut self, dir: &str) {
        self.fs_module_loader = Some(FsJsModuleLoader::new(dir))
    }
}

impl JsModuleLoader for DefaultModuleLoader {
    fn load(&mut self, module_name: &str) -> Result<String, Error> {
        if let Some(fs_loader) = &mut self.fs_module_loader {
            if let Ok(module) = fs_loader.load(module_name) {
                return Ok(module);
            }
        }
        if let Some(remote_loader) = &mut self.remote_module_loader {
            return remote_loader.load(module_name);
        }
        Err(Error::new(ErrorKind::NotFound, "failed to load module"))
    }
}