alpha2-wasm 0.5.2

Shared web preview and export wasm binding for alpha2.
Documentation
//! Asset checks for the web, shared with native (`alpha2-assets`): fingerprint matching and
//! media readiness.

use alpha2_assets::{AssetCatalog, MediaPurpose, color_pipeline_fingerprint, compare, conform};
use alpha2_model::{ColorPipeline, MediaFingerprint, Project};
use wasm_bindgen::prelude::*;

use crate::web::{internal_error, invalid_input, typed_error};

/// Compares a found fingerprint with a recorded one (both JSON `MediaFingerprint`s), returning
/// a JSON `FingerprintMatch`: `identical`, `consistent`, `mismatch` with its fields, or
/// `unverified`.
///
/// # Errors
/// Rejects JSON that is not a fingerprint, or one with a malformed part (such as an uppercase
/// hash).
#[wasm_bindgen(js_name = compareFingerprints)]
pub fn compare_fingerprints(recorded: &str, found: &str) -> Result<String, JsValue> {
    let recorded: MediaFingerprint = serde_json::from_str(recorded).map_err(invalid_input)?;
    let found: MediaFingerprint = serde_json::from_str(found).map_err(invalid_input)?;
    for (which, fingerprint) in [("recorded", &recorded), ("found", &found)] {
        if let Some(part) = fingerprint.malformed_part() {
            return Err(invalid_input(format!(
                "the {which} fingerprint has a malformed {part}"
            )));
        }
    }
    serde_json::to_string(&compare(&recorded, &found)).map_err(internal_error)
}

/// Whether `project`'s media, as `catalog` describes it, is ready for `purpose` (`preview`,
/// `preview_proxy`, `thumbnail`, `remote_edit`, `export_original`, or `export_proxy`), as a
/// JSON `ConformReport`.
///
/// # Errors
/// Rejects an invalid project, catalog (including malformed fingerprints), or purpose.
#[wasm_bindgen(js_name = conformProject)]
pub fn conform_project(project: &str, catalog: &str, purpose: &str) -> Result<String, JsValue> {
    let project = Project::from_json(project).map_err(|error| typed_error(&error))?;
    let catalog: AssetCatalog = serde_json::from_str(catalog).map_err(invalid_input)?;
    catalog.validate().map_err(invalid_input)?;
    let purpose = match purpose {
        "preview" => MediaPurpose::Preview,
        "preview_proxy" => MediaPurpose::PreviewProxy,
        "thumbnail" => MediaPurpose::Thumbnail,
        "remote_edit" => MediaPurpose::RemoteEdit,
        "export_original" => MediaPurpose::ExportOriginal,
        "export_proxy" => MediaPurpose::ExportProxy,
        other => return Err(invalid_input(format!("unknown media purpose {other}"))),
    };
    serde_json::to_string(&conform(&project, &catalog, purpose)).map_err(internal_error)
}

/// Returns a color pipeline's identity (JSON `ColorPipeline`) as a proxy that bakes it in
/// records it.
///
/// # Errors
/// Rejects JSON that is not a color pipeline.
#[wasm_bindgen(js_name = colorPipelineFingerprint)]
pub fn color_pipeline_fingerprint_js(pipeline: &str) -> Result<String, JsValue> {
    let pipeline: ColorPipeline = serde_json::from_str(pipeline).map_err(invalid_input)?;
    Ok(color_pipeline_fingerprint(&pipeline))
}