#[cfg(not(feature = "wasm"))]
use crate::check::error::ProofModeError;
use crate::check::preprocess::FetchFile;
#[cfg(feature = "wasm")]
use crate::check::preprocess::WebFile;
#[cfg(feature = "wasm")]
use js_sys::{Function, Uint8Array};
#[cfg(feature = "wasm")]
use mime_guess::Mime;
#[cfg(all(not(feature = "wasm"), feature = "reqwest"))]
use reqwest::blocking::Client;
use serde::{Deserialize, Serialize};
#[cfg(any(feature = "wasm", feature = "reqwest"))]
use std::io::Cursor;
#[cfg(feature = "wasm")]
use std::str::FromStr;
#[cfg(feature = "wasm")]
use wasm_bindgen::JsCast;
#[cfg(feature = "wasm")]
use wasm_bindgen::JsValue;
#[cfg(feature = "wasm")]
use wasm_bindgen_file_reader::WebSysFile;
#[cfg(feature = "wasm")]
use wasm_bindgen_futures::JsFuture;
#[cfg(feature = "wasm")]
use web_sys::File;
#[cfg(feature = "wasm")]
use web_sys::{Request, RequestInit, RequestMode, Response, WorkerGlobalScope};
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum MessageType {
#[serde(rename = "status")]
Status,
#[serde(rename = "error")]
Error,
#[serde(rename = "complete")]
Complete,
}
#[cfg(feature = "wasm")]
impl MessageType {
pub fn as_str(&self) -> &'static str {
match *self {
MessageType::Status => "status",
MessageType::Error => "error",
MessageType::Complete => "complete",
}
}
}
#[cfg(feature = "wasm")]
pub struct ProgressReporter {
send_message: Function,
}
#[cfg(feature = "wasm")]
impl ProgressReporter {
pub fn new(send_message: Function) -> Self {
ProgressReporter { send_message }
}
pub fn report(&self, message_type: MessageType, message: String) {
let _ = self.send_message.call2(
&JsValue::NULL,
&JsValue::from_str(message_type.as_str()),
&JsValue::from_str(message.as_str()),
);
}
}
#[cfg(not(feature = "wasm"))]
pub type ReporterCallback = std::sync::Arc<dyn Fn(MessageType, String) + Send + Sync>;
#[cfg(not(feature = "wasm"))]
pub struct ProgressReporter {
send_message: ReporterCallback,
}
#[cfg(not(feature = "wasm"))]
impl ProgressReporter {
pub fn new(send_message: ReporterCallback) -> Self {
ProgressReporter { send_message }
}
pub fn report(&self, message_type: MessageType, message: String) {
(self.send_message)(message_type, message);
}
}
#[cfg(feature = "wasm")]
pub fn translate_from_js_files(files: JsValue) -> Vec<WebFile> {
let mut input_files: Vec<WebFile> = Vec::new();
let files_array: js_sys::Array = files.into();
for i in 0..files_array.length() {
let js_file: File = files_array.get(i).unchecked_into();
let name = js_file.name();
let web_sys_file = WebSysFile::new(js_file);
let web_file = WebFile {
name,
data: web_sys_file,
error: None,
};
input_files.push(web_file);
}
input_files
}
#[cfg(feature = "wasm")]
pub async fn fetch_url(url: &str) -> Result<FetchFile, JsValue> {
let mut opts = RequestInit::new();
opts.method("GET");
opts.mode(RequestMode::Cors);
let mut name = url.split('/').last().unwrap().to_string();
let request = Request::new_with_str_and_init(url, &opts)?;
let worker_global_scope = js_sys::global().unchecked_into::<WorkerGlobalScope>();
let response = JsFuture::from(worker_global_scope.fetch_with_request(&request)).await;
if response.is_err() {
return Ok(FetchFile {
name,
data: Cursor::new(Vec::new()),
error: Some("Fetch error: disallowed by CORS".to_string()),
});
}
let response: Response = response.unwrap().dyn_into()?;
if response.status() != 200 {
return Ok(FetchFile {
name,
data: Cursor::new(Vec::new()),
error: Some(format!("Fetch error: {}", response.status_text())),
});
}
let content_type = response.headers().get("Content-Type").unwrap().unwrap();
let mime_type = Mime::from_str(&content_type).unwrap();
let extension = mime_type.subtype().as_str();
let array_buffer = JsFuture::from(response.array_buffer()?).await?;
let uint8_array = Uint8Array::new(&array_buffer);
if !name.contains('.') {
name = format!("{}.{}", name, extension);
}
let data = Cursor::new(uint8_array.to_vec());
let web_file = FetchFile {
name,
data,
error: None,
};
Ok(web_file)
}
#[cfg(all(not(feature = "wasm"), feature = "reqwest"))]
pub fn fetch_url(url: &str) -> Result<FetchFile, ProofModeError> {
let client = Client::new();
let response = client.get(url).send()?;
let bytes = response.bytes().unwrap();
let file = FetchFile {
name: url.to_string(),
data: Cursor::new(bytes.to_vec()),
error: None,
};
Ok(file)
}
#[cfg(all(not(feature = "wasm"), not(feature = "reqwest")))]
pub fn fetch_url(_url: &str) -> Result<FetchFile, ProofModeError> {
Err(ProofModeError::Failure {
message: "URL fetching not supported without reqwest feature".to_string(),
})
}