proofmode 0.9.0

Capture, share, and preserve verifiable photos and videos
Documentation
use crate::check::utils::MessageType;
use crate::generate::core::PlatformCallbacks;
use crate::generate_error;
use crate::generate_types::*;
use std::collections::HashMap;
use std::sync::Arc;

// Error type for UniFFI
#[derive(Debug, thiserror::Error, uniffi::Error)]
pub enum ProofModeError {
    #[error("IO error: {message}")]
    Io { message: String },
    #[error("Generation error: {message}")]
    Generation { message: String },
    #[error("Check error: {message}")]
    Check { message: String },
}

impl From<generate_error::ProofModeError> for ProofModeError {
    fn from(err: generate_error::ProofModeError) -> Self {
        ProofModeError::Generation {
            message: err.to_string(),
        }
    }
}

// Callback trait that foreign language bindings (Kotlin/Swift/Python/Ruby) implement
#[uniffi::export(with_foreign)]
pub trait ProofModeCallbacks: Send + Sync {
    fn get_location(&self) -> Option<LocationData>;
    fn get_device_info(&self) -> Option<DeviceData>;
    fn get_network_info(&self) -> Option<NetworkData>;
    fn save_data(&self, hash: String, filename: String, data: Vec<u8>) -> bool;
    fn save_text(&self, hash: String, filename: String, text: String) -> bool;
    fn sign_data(&self, data: Vec<u8>) -> Option<Vec<u8>>;
    fn report_progress(&self, message: String);
}

// Adapter that bridges ProofModeCallbacks -> PlatformCallbacks (used by ProofGenerator)
struct CallbacksAdapter {
    inner: Arc<dyn ProofModeCallbacks>,
}

impl CallbacksAdapter {
    fn new(callbacks: Arc<dyn ProofModeCallbacks>) -> Self {
        Self { inner: callbacks }
    }
}

impl PlatformCallbacks for CallbacksAdapter {
    fn get_device_info(&self) -> Option<DeviceData> {
        self.inner.get_device_info()
    }

    fn get_location_info(&self) -> Option<LocationData> {
        self.inner.get_location()
    }

    fn get_network_info(&self) -> Option<NetworkData> {
        self.inner.get_network_info()
    }

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

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

    fn sign_data(&self, data: &[u8]) -> generate_error::Result<Option<Vec<u8>>> {
        Ok(self.inner.sign_data(data.to_vec()))
    }

    fn notarize_hash(&self, _hash: &str) -> generate_error::Result<Option<NotarizationData>> {
        Ok(None)
    }

    fn report_progress(&self, message: &str) {
        self.inner.report_progress(message.to_string());
    }
}

// Generate proof using ProofGenerator with callbacks
#[uniffi::export]
pub fn generate_proof(
    media_data: Vec<u8>,
    metadata: HashMap<String, String>,
    config: ProofModeConfig,
    callbacks: Arc<dyn ProofModeCallbacks>,
) -> Result<String, ProofModeError> {
    callbacks.report_progress("Starting proof generation...".to_string());

    let adapter = CallbacksAdapter::new(Arc::clone(&callbacks));
    let generator = crate::generate::core::ProofGenerator::new(config);

    callbacks.report_progress("Generating proof...".to_string());
    let hash = generator.generate_proof(&media_data, metadata, &adapter)?;

    callbacks.report_progress(format!("Proof generation complete: {}", hash));
    Ok(hash)
}

// Hash calculation
#[uniffi::export]
pub fn get_file_hash(media_data: Vec<u8>) -> String {
    crate::crypto::hash::calculate_hash(&media_data)
}

// Version information
#[uniffi::export]
pub fn get_version() -> String {
    env!("CARGO_PKG_VERSION").to_string()
}

// Check files with progress forwarding
#[uniffi::export]
pub fn check_files(
    file_paths: Vec<String>,
    callbacks: Arc<dyn ProofModeCallbacks>,
) -> Result<String, ProofModeError> {
    callbacks.report_progress("Starting file verification...".to_string());

    let progress_cb = Arc::clone(&callbacks);
    let reporter: crate::check::utils::ReporterCallback =
        Arc::new(move |msg_type: MessageType, msg: String| {
            let prefix = match msg_type {
                MessageType::Status => "STATUS",
                MessageType::Error => "ERROR",
                MessageType::Complete => "COMPLETE",
            };
            progress_cb.report_progress(format!("{}: {}", prefix, msg));
        });

    let result =
        crate::check::check_files(&file_paths, reporter).map_err(|e| ProofModeError::Check {
            message: e.to_string(),
        })?;

    let json = serde_json::to_string(&result).map_err(|e| ProofModeError::Generation {
        message: e.to_string(),
    })?;

    callbacks.report_progress("Verification complete!".to_string());
    Ok(json)
}