feat: implement hash related primops

This commit is contained in:
2026-02-15 18:16:50 +08:00
parent 7836f8c869
commit 5c48e5cfdd
7 changed files with 173 additions and 30 deletions

View File

@@ -37,7 +37,7 @@ deno_error = "0.7"
nix-nar = "0.3"
sha2 = "0.10"
sha1 = "0.10"
md5 = "0.7"
md5 = "0.8"
hex = "0.4"
base64 = "0.22"

View File

@@ -1,21 +1,37 @@
import { forceStringNoCtx } from "../type-assert";
import { forceAttrs, forceStringNoCtx, forceStringValue } from "../type-assert";
import type { NixValue } from "../types";
import { realisePath } from "./io";
export const hashFile =
(type: NixValue) =>
(p: NixValue): string => {
const _ty = forceStringNoCtx(type);
const _pathStr = realisePath(p);
throw new Error("Not implemented: hashFile");
const algo = forceStringNoCtx(type);
const pathStr = realisePath(p);
return Deno.core.ops.op_hash_file(algo, pathStr);
};
export const hashString =
(_type: NixValue) =>
(_p: NixValue): never => {
throw new Error("Not implemented: hashString");
(type: NixValue) =>
(s: NixValue): string => {
const algo = forceStringNoCtx(type);
const data = forceStringValue(s);
return Deno.core.ops.op_hash_string(algo, data);
};
export const convertHash = (_args: NixValue): never => {
throw new Error("Not implemented: convertHash");
export const convertHash = (args: NixValue): string => {
const attrs = forceAttrs(args);
const hash = forceStringNoCtx(attrs.hash);
let hashAlgo: string | null = null;
if ("hashAlgo" in attrs) {
hashAlgo = forceStringNoCtx(attrs.hashAlgo);
}
const toHashFormat = forceStringNoCtx(attrs.toHashFormat);
return Deno.core.ops.op_convert_hash({
hash,
hashAlgo,
toHashFormat,
});
};

View File

@@ -20,6 +20,13 @@ declare global {
function op_make_placeholder(output: string): string;
function op_store_path(path: string): string;
function op_convert_hash(input: {
hash: string;
hashAlgo: string | null;
toHashFormat: string;
}): string;
function op_hash_string(algo: string, data: string): string;
function op_hash_file(algo: string, path: string): string;
function op_parse_hash(hashStr: string, algo: string | null): { hex: string; algo: string };
function op_add_path(

View File

@@ -58,6 +58,9 @@ fn runtime_extension<Ctx: RuntimeContext>() -> Extension {
op_make_placeholder(),
op_store_path::<Ctx>(),
op_convert_hash(),
op_hash_string(),
op_hash_file(),
op_parse_hash(),
op_add_path::<Ctx>(),

View File

@@ -1106,3 +1106,135 @@ fn op_make_fixed_output_path_impl(
crate::nix_utils::make_store_path(store_dir, "output:out", &inner_hash, name)
}
}
#[deno_core::op2]
#[string]
pub(super) fn op_hash_string(
#[string] algo: String,
#[string] data: String,
) -> std::result::Result<String, NixRuntimeError> {
use sha2::{Digest, Sha256, Sha512};
let hash_bytes: Vec<u8> = match algo.as_str() {
"sha256" => {
let mut hasher = Sha256::new();
hasher.update(data.as_bytes());
hasher.finalize().to_vec()
}
"sha512" => {
let mut hasher = Sha512::new();
hasher.update(data.as_bytes());
hasher.finalize().to_vec()
}
"sha1" => {
use sha1::Digest as _;
let mut hasher = sha1::Sha1::new();
hasher.update(data.as_bytes());
hasher.finalize().to_vec()
}
"md5" => {
let digest = md5::compute(data.as_bytes());
digest.to_vec()
}
_ => {
return Err(NixRuntimeError::from(format!(
"unknown hash algorithm '{}'",
algo
)));
}
};
Ok(hex::encode(hash_bytes))
}
#[deno_core::op2]
#[string]
pub(super) fn op_hash_file(
#[string] algo: String,
#[string] path: String,
) -> std::result::Result<String, NixRuntimeError> {
let data = std::fs::read(&path)
.map_err(|e| NixRuntimeError::from(format!("cannot read '{}': {}", path, e)))?;
use sha2::{Digest, Sha256, Sha512};
let hash_bytes: Vec<u8> = match algo.as_str() {
"sha256" => {
let mut hasher = Sha256::new();
hasher.update(&data);
hasher.finalize().to_vec()
}
"sha512" => {
let mut hasher = Sha512::new();
hasher.update(&data);
hasher.finalize().to_vec()
}
"sha1" => {
use sha1::Digest as _;
let mut hasher = sha1::Sha1::new();
hasher.update(&data);
hasher.finalize().to_vec()
}
"md5" => {
let digest = md5::compute(&data);
digest.to_vec()
}
_ => {
return Err(NixRuntimeError::from(format!(
"unknown hash algorithm '{}'",
algo
)));
}
};
Ok(hex::encode(hash_bytes))
}
#[deno_core::op2]
#[string]
pub(super) fn op_convert_hash(
#[serde] input: ConvertHashInput,
) -> std::result::Result<String, NixRuntimeError> {
use nix_compat::nixhash::{HashAlgo, NixHash};
let hash_algo = input
.hash_algo
.as_deref()
.and_then(|a| HashAlgo::from_str(a).ok());
let hash = NixHash::from_str(&input.hash, hash_algo).map_err(|e| {
NixRuntimeError::from(format!("cannot convert hash '{}': {}", input.hash, e))
})?;
let bytes = hash.digest_as_bytes();
match input.to_format.as_str() {
"base16" => Ok(hex::encode(bytes)),
"nix32" | "base32" => Ok(nix_compat::nixbase32::encode(bytes)),
"base64" => {
use base64::Engine as _;
Ok(base64::engine::general_purpose::STANDARD.encode(bytes))
}
"sri" => Ok(format!(
"{}-{}",
hash.algo(),
{
use base64::Engine as _;
base64::engine::general_purpose::STANDARD.encode(bytes)
}
)),
_ => Err(NixRuntimeError::from(format!(
"unknown hash format '{}'",
input.to_format
))),
}
}
#[derive(serde::Deserialize)]
pub(super) struct ConvertHashInput {
hash: String,
#[serde(rename = "hashAlgo")]
hash_algo: Option<String>,
#[serde(rename = "toHashFormat")]
to_format: String,
}

View File

@@ -122,10 +122,7 @@ eval_okay_test!(concatmap);
eval_okay_test!(concatstringssep);
eval_okay_test!(context);
eval_okay_test!(context_introspection);
eval_okay_test!(
#[ignore = "not implemented: convertHash"]
convertHash
);
eval_okay_test!(convertHash);
eval_okay_test!(curpos);
eval_okay_test!(deepseq);
eval_okay_test!(delayed_with);
@@ -158,24 +155,15 @@ eval_okay_test!(
fromTOML_timestamps
);
eval_okay_test!(functionargs);
eval_okay_test!(
#[ignore = "not implemented: hashFile"]
hashfile
);
eval_okay_test!(
#[ignore = "not implemented: hashString"]
hashstring
);
eval_okay_test!(hashfile);
eval_okay_test!(hashstring);
eval_okay_test!(getattrpos);
eval_okay_test!(getattrpos_functionargs);
eval_okay_test!(getattrpos_undefined);
eval_okay_test!(getenv, || {
unsafe { std::env::set_var("TEST_VAR", "foo") };
});
eval_okay_test!(
#[ignore = "not implemented: hashString"]
groupBy
);
eval_okay_test!(groupBy);
eval_okay_test!(r#if);
eval_okay_test!(ind_string);
eval_okay_test!(import);
@@ -265,10 +253,7 @@ eval_okay_test!(tryeval);
eval_okay_test!(types);
eval_okay_test!(versions);
eval_okay_test!(with);
eval_okay_test!(
#[ignore = "not implemented: hashString"]
zipAttrsWith
);
eval_okay_test!(zipAttrsWith);
eval_fail_test!(fail_abort);
eval_fail_test!(fail_addDrvOutputDependencies_empty_context);

Binary file not shown.