candding 0.1.0

Pure candle embeddings for Rust with no ONNX: dense, sparse, multi-vector and rerank models today, image models on the roadmap
Documentation
use std::fs;

use serde_json::json;
use tempfile::tempdir;

use super::ID;
use super::SHA;
use super::config;
use super::server::Reply;
use super::server::Server;
use crate::error::CanddingError;
use crate::hub::download::fetch;
use crate::registry::WeightsFormat;

const NOT_A_COMMIT: &str = "is not a commit of 40 lowercase hexadecimal characters";

fn hub_naming(
  commit: &'static str,
  listed: String,
) -> impl Fn(&str, &str) -> Reply + Send + Sync + 'static {
  move |_, path| {
    let path = path.split('?').next().unwrap_or(path);
    if path == format!("/api/models/{ID}/revision/main") {
      return Reply::new(200, json!({ "id": ID, "sha": commit }).to_string());
    }
    if path.starts_with(&format!("/api/models/{ID}/tree/")) {
      let entry = |path: &str| json!({ "type": "file", "oid": "f", "size": 15, "path": path });
      return Reply::new(
        200,
        json!([entry("config.json"), entry(&listed)]).to_string(),
      );
    }
    Reply::new(200, "attacker bytes\n")
      .header("ETag", "\"blob-evil\"")
      .header("X-Repo-Commit", commit)
  }
}

/// The error, the requests the hub saw and how many entries besides candding's lock the cache root holds afterwards.
fn refused(commit: &'static str, listed: &str) -> (CanddingError, Vec<String>, usize) {
  let server = Server::start(hub_naming(commit, listed.to_string()));
  let cache = tempdir().unwrap();
  let config = config(cache.path(), &server.url);
  let error = fetch(&config, ID, "main", WeightsFormat::Safetensors).unwrap_err();
  let written = fs::read_dir(cache.path())
    .unwrap()
    .flatten()
    .filter(|entry| entry.file_name() != ".candding")
    .count();
  (error, server.requests(), written)
}

#[test]
fn a_listed_path_that_would_leave_the_snapshot_is_refused_before_any_write() {
  for (listed, expected) in [
    ("/etc/candding/config.json", "is absolute"),
    ("1_Pooling\\config.json", "contains a backslash"),
    ("1_Pooling//config.json", "has an empty component"),
    ("config.json/", "has an empty component"),
    ("1_Pooling/../../../../escaped.json", "has a `..` component"),
    ("onnx/../../model.onnx", "has a `..` component"),
    ("./config.json", "has a `.` component"),
    ("1_Pooling/./config.json", "has a `.` component"),
    ("1_Pooling/.", "has a `.` component"),
  ] {
    let (error, requests, written) = refused(SHA, listed);
    match error {
      CanddingError::UnsafeHubPath { id, path, reason } => {
        assert_eq!((id.as_str(), path.as_str()), (ID, listed));
        assert_eq!(reason, expected, "{listed}");
      }
      other => panic!("{listed}: {other:?}"),
    }
    assert!(
      !requests.iter().any(|request| request.contains("/resolve/")),
      "{listed}: {requests:?}"
    );
    assert_eq!(
      written, 0,
      "{listed}: something besides the lock was written"
    );
  }
}

#[test]
fn a_commit_that_is_not_40_lowercase_hex_characters_is_refused_before_the_listing() {
  for commit in [
    "../../../sha-escaped",
    "0123456789abcdef0123456789abcdef0123456",
    "0123456789abcdef0123456789abcdef012345678",
    "0123456789ABCDEF0123456789abcdef01234567",
    "0123456789abcdef0123456789abcdef01/../..",
  ] {
    let (error, requests, written) = refused(commit, "config.json");
    match error {
      CanddingError::UnsafeHubPath { id, path, reason } => {
        assert_eq!((id.as_str(), path.as_str()), (ID, commit));
        assert_eq!(reason, NOT_A_COMMIT, "{commit}");
      }
      other => panic!("{commit}: {other:?}"),
    }
    assert_eq!(requests.len(), 1, "{commit}: {requests:?}");
    assert_eq!(
      written, 0,
      "{commit}: something besides the lock was written"
    );
  }
}

#[test]
fn the_unsafe_path_error_names_the_model_the_path_and_the_reason() {
  let (error, _, _) = refused(SHA, "1_Pooling/../x.json");
  assert_eq!(
    error.to_string(),
    "the hub's answer for `org/model` names `1_Pooling/../x.json`, which has a `..` component; nothing was downloaded"
  );
}