proofmode 0.9.0

Capture, share, and preserve verifiable photos and videos
Documentation
#[cfg(feature = "wasm")]
use crate::generate::core::PlatformCallbacks;
#[cfg(feature = "wasm")]
use crate::generate_error::{ProofModeError, Result};
#[cfg(feature = "wasm")]
use crate::generate_types::*;
#[cfg(feature = "wasm")]
use std::collections::HashMap;
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::*;

#[cfg(feature = "wasm")]
#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(js_namespace = console)]
    fn log(s: &str);

    type ProofCallbacks;

    #[wasm_bindgen(method, js_name = getDeviceInfo)]
    fn get_device_info(this: &ProofCallbacks) -> JsValue;

    #[wasm_bindgen(method, js_name = getLocationInfo)]
    fn get_location_info(this: &ProofCallbacks) -> JsValue;

    #[wasm_bindgen(method, js_name = getNetworkInfo)]
    fn get_network_info(this: &ProofCallbacks) -> JsValue;

    #[wasm_bindgen(method, js_name = saveData)]
    fn save_data(this: &ProofCallbacks, hash: &str, filename: &str, data: &[u8]) -> bool;

    #[wasm_bindgen(method, js_name = saveText)]
    fn save_text(this: &ProofCallbacks, hash: &str, filename: &str, text: &str) -> bool;

    #[wasm_bindgen(method, js_name = signData)]
    fn sign_data(this: &ProofCallbacks, data: &[u8]) -> JsValue;

    #[wasm_bindgen(method, js_name = notarizeHash)]
    fn notarize_hash(this: &ProofCallbacks, hash: &str) -> JsValue;
}

#[cfg(feature = "wasm")]
pub struct WasmCallbacks {
    js_callbacks: ProofCallbacks,
}

#[cfg(feature = "wasm")]
impl WasmCallbacks {
    pub fn new(js_callbacks: ProofCallbacks) -> Self {
        Self { js_callbacks }
    }
}

#[cfg(feature = "wasm")]
impl PlatformCallbacks for WasmCallbacks {
    fn get_device_info(&self) -> Option<DeviceData> {
        let js_value = self.js_callbacks.get_device_info();
        if js_value.is_null() || js_value.is_undefined() {
            None
        } else {
            serde_wasm_bindgen::from_value(js_value).ok()
        }
    }

    fn get_location_info(&self) -> Option<LocationData> {
        let js_value = self.js_callbacks.get_location_info();
        if js_value.is_null() || js_value.is_undefined() {
            None
        } else {
            serde_wasm_bindgen::from_value(js_value).ok()
        }
    }

    fn get_network_info(&self) -> Option<NetworkData> {
        let js_value = self.js_callbacks.get_network_info();
        if js_value.is_null() || js_value.is_undefined() {
            None
        } else {
            serde_wasm_bindgen::from_value(js_value).ok()
        }
    }

    fn save_data(&self, hash: &str, filename: &str, data: &[u8]) -> Result<()> {
        if self.js_callbacks.save_data(hash, filename, data) {
            Ok(())
        } else {
            Err(ProofModeError::Storage(
                "Failed to save data via JS callback".to_string(),
            ))
        }
    }

    fn save_text(&self, hash: &str, filename: &str, text: &str) -> Result<()> {
        if self.js_callbacks.save_text(hash, filename, text) {
            Ok(())
        } else {
            Err(ProofModeError::Storage(
                "Failed to save text via JS callback".to_string(),
            ))
        }
    }

    fn sign_data(&self, data: &[u8]) -> Result<Option<Vec<u8>>> {
        let js_value = self.js_callbacks.sign_data(data);
        if js_value.is_null() || js_value.is_undefined() {
            Ok(None)
        } else {
            match serde_wasm_bindgen::from_value::<Vec<u8>>(js_value) {
                Ok(data) => Ok(Some(data)),
                Err(_) => Ok(None),
            }
        }
    }

    fn notarize_hash(&self, hash: &str) -> Result<Option<NotarizationData>> {
        let js_value = self.js_callbacks.notarize_hash(hash);
        if js_value.is_null() || js_value.is_undefined() {
            Ok(None)
        } else {
            match serde_wasm_bindgen::from_value(js_value) {
                Ok(data) => Ok(Some(data)),
                Err(_) => Ok(None),
            }
        }
    }

    fn report_progress(&self, _message: &str) {
        // WASM logging could be added here via web_sys::console::log_1
    }
}

#[cfg(feature = "wasm")]
#[wasm_bindgen]
pub fn generate_proof_wasm(
    media_data: &[u8],
    metadata_json: &str,
    js_callbacks: ProofCallbacks,
) -> std::result::Result<String, JsValue> {
    let metadata: HashMap<String, String> = serde_json::from_str(metadata_json)
        .map_err(|e| JsValue::from_str(&format!("Invalid metadata JSON: {}", e)))?;

    // Check if we have location data in metadata to enable location tracking
    let has_location = metadata.contains_key("latitude") && metadata.contains_key("longitude");

    let config = ProofModeConfig {
        auto_notarize: false,
        track_location: has_location,
        track_device_id: true,
        track_network: true,
        add_credentials: false,
        embed_c2pa: false,
    };

    let callbacks = WasmCallbacks::new(js_callbacks);
    let generator = crate::generate::core::ProofGenerator::new(config);

    match generator.generate_proof(media_data, metadata, &callbacks) {
        Ok(hash) => Ok(hash),
        Err(e) => Err(JsValue::from_str(&format!(
            "Proof generation failed: {}",
            e
        ))),
    }
}

#[cfg(feature = "wasm")]
#[wasm_bindgen]
pub fn get_file_hash(media_data: &[u8]) -> String {
    crate::crypto::hash::calculate_hash(media_data)
}