saccade 0.2.7

Visual-regression CLI for renderers and graphics code, built on NVIDIA FLIP
//! Root-contained arm validity mirror on the measurement tool.
use crate::agent::CliError;
use saccade_core::root_policy::RootPolicy;
use serde::Deserialize;
use serde_json::{Value, json};
use std::path::{Path, PathBuf};
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct Args {
    operation: String,
    a: PathBuf,
    b: PathBuf,
    fingerprint_map: Option<PathBuf>,
    max_record_bytes: Option<u64>,
    compare: Option<saccade_core::arms::CompareMode>,
    #[serde(default)]
    vary: Vec<String>,
    #[serde(default)]
    ignore: Vec<String>,
    #[serde(default)]
    allow_unreached: Vec<String>,
    meta_name: Option<String>,
}
pub(crate) fn validate_map(
    policy: &RootPolicy,
    path: &Path,
    inputs: &[&Path],
) -> Result<(), CliError> {
    let map = saccade_core::arms::FingerprintMap::read(&policy.read(path)?)?;
    for input in inputs {
        let root = if input.is_dir() {
            *input
        } else {
            input.parent().unwrap_or(Path::new("."))
        };
        for file in &map.record_files {
            let record = root.join(file);
            if record.exists() {
                policy.read(&record)?;
            }
        }
        for s in map
            .fields
            .values()
            .chain(map.readiness.iter().flat_map(|r| [&r.reached, &r.observed]))
        {
            if let Some(file) = &s.file {
                let sibling = root.join(file);
                if sibling.exists() {
                    policy.read(&sibling)?;
                }
            }
        }
    }
    Ok(())
}
pub(crate) fn call(policy: &RootPolicy, value: Value) -> Result<Value, CliError> {
    let args: Args = serde_json::from_value(value)?;
    if args.operation != "arms_check" {
        return Err(CliError::usage("expected arms_check"));
    }
    let a = policy.read(&args.a)?;
    let b = policy.read(&args.b)?;
    let opts = saccade_core::meta::MetaOptions {
        max_record_bytes: args.max_record_bytes,
        compare: args.compare,
        intended: args.vary,
        allow_unreached: args.allow_unreached,
        ignore: args.ignore,
        fingerprint_map: args.fingerprint_map.map(|p| policy.read(&p)).transpose()?,
        name: args
            .meta_name
            .unwrap_or_else(|| saccade_core::meta::DEFAULT_META_NAME.into()),
        ..Default::default()
    };
    if let Some(map) = &opts.fingerprint_map {
        validate_map(policy, map, &[&a, &b])?;
    }
    let cfg = saccade_core::config::RunConfig {
        meta: opts,
        ..Default::default()
    };
    Ok(serde_json::to_value(saccade_core::arms::validate_paths(
        &a, &b, &cfg,
    )?)?)
}
pub(crate) fn schema() -> Value {
    json!({"type":"object","additionalProperties":false,"required":["operation","a","b"],"properties":{"operation":{"const":"arms_check"},"a":{"type":"string"},"b":{"type":"string"},"vary":{"type":"array","items":{"type":"string"}},"ignore":{"type":"array","items":{"type":"string"}},"compare":{"enum":["mapped_only","all"]},"fingerprint_map":{"type":"string"},"max_record_bytes":{"type":"integer","minimum":1,"maximum":saccade_core::arms::HARD_MAX_RECORD_BYTES},"meta_name":{"type":"string"},"allow_unreached":{"type":"array","items":{"type":"string"}}}})
}