proofmode 0.9.0

Capture, share, and preserve verifiable photos and videos
Documentation
#[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(),
    })
}