refactor(nix-compat/nixhash): move from_ and to_ to NixHash struct
It was a bit confusing to construct NixHash, having them as separate functions in the module itself, rather than in the NixHash impl. Also the names were very inconsistent. This renames parsers to `from_$format_$encoding` and format methods to `to_$format_$encoding`. It also adds / moves around a few docstrings, explaining the formats and encodings in the struct docstring itself. from_str is changed to accept Option<HashAlgo>, not Option<&str>, and the otherwise unused `from_nix_hash_string` is folded into from_str. We also simply use from_sri in from_str, as the error path there doesn't allocate anymore. Similarly, the from_nix_str function was only a helper function used to parse a subset of the formats supported in the NixHash::from_str method. We shouldn't be using it outside of there, all usages (only in tests) have been replaced with NixHash::from_algo_and_digest. Change-Id: I36128839dbef19c58b55d5dc5817e38e37a483cc Reviewed-on: https://cl.snix.dev/c/snix/+/30554 Reviewed-by: Ilan Joselevich <personal@ilanjoselevich.com> Reviewed-by: edef <edef@edef.eu> Tested-by: besadii
This commit is contained in:
parent
6c1bfd778e
commit
f6c66af33d
14 changed files with 280 additions and 265 deletions
|
|
@ -4,7 +4,7 @@ use crate::known_paths::KnownPaths;
|
||||||
use crate::snix_store_io::SnixStoreIO;
|
use crate::snix_store_io::SnixStoreIO;
|
||||||
use bstr::BString;
|
use bstr::BString;
|
||||||
use nix_compat::derivation::{Derivation, Output};
|
use nix_compat::derivation::{Derivation, Output};
|
||||||
use nix_compat::nixhash;
|
use nix_compat::nixhash::{CAHash, HashAlgo, NixHash};
|
||||||
use nix_compat::store_path::{StorePath, StorePathRef};
|
use nix_compat::store_path::{StorePath, StorePathRef};
|
||||||
use snix_eval::builtin_macros::builtins;
|
use snix_eval::builtin_macros::builtins;
|
||||||
use snix_eval::generators::{self, GenCo, emit_warning_kind};
|
use snix_eval::generators::{self, GenCo, emit_warning_kind};
|
||||||
|
|
@ -132,9 +132,14 @@ fn handle_fixed_output(
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// construct a NixHash.
|
let hash_algo = hash_algo_str
|
||||||
let nixhash = nixhash::from_str(&hash_str, hash_algo_str.as_deref())
|
.map(|s| HashAlgo::try_from(s.as_str()))
|
||||||
|
.transpose()
|
||||||
.map_err(DerivationError::InvalidOutputHash)?;
|
.map_err(DerivationError::InvalidOutputHash)?;
|
||||||
|
|
||||||
|
// construct a NixHash.
|
||||||
|
let nixhash =
|
||||||
|
NixHash::from_str(&hash_str, hash_algo).map_err(DerivationError::InvalidOutputHash)?;
|
||||||
let algo = nixhash.algo();
|
let algo = nixhash.algo();
|
||||||
|
|
||||||
// construct the fixed output.
|
// construct the fixed output.
|
||||||
|
|
@ -143,8 +148,8 @@ fn handle_fixed_output(
|
||||||
Output {
|
Output {
|
||||||
path: None,
|
path: None,
|
||||||
ca_hash: match hash_mode_str.as_deref() {
|
ca_hash: match hash_mode_str.as_deref() {
|
||||||
None | Some("flat") => Some(nixhash::CAHash::Flat(nixhash)),
|
None | Some("flat") => Some(CAHash::Flat(nixhash)),
|
||||||
Some("recursive") => Some(nixhash::CAHash::Nar(nixhash)),
|
Some("recursive") => Some(CAHash::Nar(nixhash)),
|
||||||
Some(other) => {
|
Some(other) => {
|
||||||
return Err(DerivationError::InvalidOutputHashMode(other.to_string()))?;
|
return Err(DerivationError::InvalidOutputHashMode(other.to_string()))?;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use crate::{
|
||||||
fetchers::{Fetch, url_basename},
|
fetchers::{Fetch, url_basename},
|
||||||
snix_store_io::SnixStoreIO,
|
snix_store_io::SnixStoreIO,
|
||||||
};
|
};
|
||||||
use nix_compat::nixhash;
|
use nix_compat::nixhash::{HashAlgo, NixHash};
|
||||||
use snix_eval::builtin_macros::builtins;
|
use snix_eval::builtin_macros::builtins;
|
||||||
use snix_eval::generators::Gen;
|
use snix_eval::generators::Gen;
|
||||||
use snix_eval::generators::GenCo;
|
use snix_eval::generators::GenCo;
|
||||||
|
|
@ -70,15 +70,13 @@ async fn extract_fetch_args(
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse the sha256 string into a digest.
|
// parse the sha256 string into a digest.
|
||||||
let sha256 = match sha256_str {
|
let sha256 = sha256_str
|
||||||
Some(sha256_str) => {
|
.map(|x| {
|
||||||
let nixhash = nixhash::from_str(&sha256_str, Some("sha256"))
|
NixHash::from_str(&x, Some(HashAlgo::Sha256))
|
||||||
.map_err(|e| ErrorKind::InvalidHash(e.to_string()))?;
|
.map(|x| x.digest_as_bytes().try_into().expect("is sha256"))
|
||||||
|
.map_err(|e| ErrorKind::InvalidHash(e.to_string()))
|
||||||
Some(nixhash.digest_as_bytes().try_into().expect("is sha256"))
|
})
|
||||||
}
|
.transpose()?;
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the URL.
|
// Parse the URL.
|
||||||
let url = Url::parse(&url_str).map_err(|e| ErrorKind::SnixError(Rc::new(e)))?;
|
let url = Url::parse(&url_str).map_err(|e| ErrorKind::SnixError(Rc::new(e)))?;
|
||||||
|
|
@ -90,7 +88,7 @@ async fn extract_fetch_args(
|
||||||
#[builtins(state = "Rc<SnixStoreIO>")]
|
#[builtins(state = "Rc<SnixStoreIO>")]
|
||||||
pub(crate) mod fetcher_builtins {
|
pub(crate) mod fetcher_builtins {
|
||||||
use bstr::ByteSlice;
|
use bstr::ByteSlice;
|
||||||
use nix_compat::flakeref;
|
use nix_compat::{flakeref, nixhash::NixHash};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
@ -154,7 +152,7 @@ pub(crate) mod fetcher_builtins {
|
||||||
name,
|
name,
|
||||||
Fetch::URL {
|
Fetch::URL {
|
||||||
url: args.url,
|
url: args.url,
|
||||||
exp_hash: args.sha256.map(nixhash::NixHash::Sha256),
|
exp_hash: args.sha256.map(NixHash::Sha256),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ mod import_builtins {
|
||||||
use crate::builtins::ImportError;
|
use crate::builtins::ImportError;
|
||||||
use crate::snix_store_io::SnixStoreIO;
|
use crate::snix_store_io::SnixStoreIO;
|
||||||
use bstr::ByteSlice;
|
use bstr::ByteSlice;
|
||||||
use nix_compat::nixhash::{CAHash, NixHash};
|
use nix_compat::nixhash::{CAHash, HashAlgo, NixHash};
|
||||||
use nix_compat::store_path::{StorePath, StorePathRef, build_ca_path};
|
use nix_compat::store_path::{StorePath, StorePathRef, build_ca_path};
|
||||||
use sha2::Digest;
|
use sha2::Digest;
|
||||||
use snix_castore::blobservice::BlobService;
|
use snix_castore::blobservice::BlobService;
|
||||||
|
|
@ -368,7 +368,7 @@ mod import_builtins {
|
||||||
.select("sha256")
|
.select("sha256")
|
||||||
.map(|h| {
|
.map(|h| {
|
||||||
h.to_str().and_then(|expected| {
|
h.to_str().and_then(|expected| {
|
||||||
match nix_compat::nixhash::from_str(expected.to_str()?, Some("sha256")) {
|
match NixHash::from_str(expected.to_str()?, Some(HashAlgo::Sha256)) {
|
||||||
Ok(NixHash::Sha256(digest)) => Ok(digest),
|
Ok(NixHash::Sha256(digest)) => Ok(digest),
|
||||||
Ok(_) => unreachable!(),
|
Ok(_) => unreachable!(),
|
||||||
Err(e) => Err(ErrorKind::InvalidHash(e.to_string())),
|
Err(e) => Err(ErrorKind::InvalidHash(e.to_string())),
|
||||||
|
|
|
||||||
|
|
@ -603,7 +603,7 @@ mod tests {
|
||||||
mod fetch {
|
mod fetch {
|
||||||
use super::super::*;
|
use super::super::*;
|
||||||
use crate::fetchers::Fetch;
|
use crate::fetchers::Fetch;
|
||||||
use nix_compat::{nixbase32, nixhash};
|
use nix_compat::{nixbase32, nixhash::NixHash};
|
||||||
use rstest::rstest;
|
use rstest::rstest;
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
|
|
@ -618,7 +618,7 @@ mod tests {
|
||||||
#[case::url_sha256(
|
#[case::url_sha256(
|
||||||
Fetch::URL{
|
Fetch::URL{
|
||||||
url: Url::parse("https://raw.githubusercontent.com/aaptel/notmuch-extract-patch/f732a53e12a7c91a06755ebfab2007adc9b3063b/notmuch-extract-patch").unwrap(),
|
url: Url::parse("https://raw.githubusercontent.com/aaptel/notmuch-extract-patch/f732a53e12a7c91a06755ebfab2007adc9b3063b/notmuch-extract-patch").unwrap(),
|
||||||
exp_hash: Some(nixhash::from_sri_str("sha256-Xa1Jbl2Eq5+L0ww+Ph1osA3Z/Dxe/RkN1/dITQCdXFk=").unwrap()),
|
exp_hash: Some(NixHash::from_sri("sha256-Xa1Jbl2Eq5+L0ww+Ph1osA3Z/Dxe/RkN1/dITQCdXFk=").unwrap()),
|
||||||
},
|
},
|
||||||
Some(StorePathRef::from_bytes(b"06qi00hylriyfm0nl827crgjvbax84mz-notmuch-extract-patch").unwrap()),
|
Some(StorePathRef::from_bytes(b"06qi00hylriyfm0nl827crgjvbax84mz-notmuch-extract-patch").unwrap()),
|
||||||
"notmuch-extract-patch"
|
"notmuch-extract-patch"
|
||||||
|
|
@ -626,7 +626,7 @@ mod tests {
|
||||||
#[case::url_custom_name(
|
#[case::url_custom_name(
|
||||||
Fetch::URL{
|
Fetch::URL{
|
||||||
url: Url::parse("https://test.example/owo").unwrap(),
|
url: Url::parse("https://test.example/owo").unwrap(),
|
||||||
exp_hash: Some(nixhash::from_sri_str("sha256-Xa1Jbl2Eq5+L0ww+Ph1osA3Z/Dxe/RkN1/dITQCdXFk=").unwrap()),
|
exp_hash: Some(NixHash::from_sri("sha256-Xa1Jbl2Eq5+L0ww+Ph1osA3Z/Dxe/RkN1/dITQCdXFk=").unwrap()),
|
||||||
},
|
},
|
||||||
Some(StorePathRef::from_bytes(b"06qi00hylriyfm0nl827crgjvbax84mz-notmuch-extract-patch").unwrap()),
|
Some(StorePathRef::from_bytes(b"06qi00hylriyfm0nl827crgjvbax84mz-notmuch-extract-patch").unwrap()),
|
||||||
"notmuch-extract-patch"
|
"notmuch-extract-patch"
|
||||||
|
|
@ -634,7 +634,7 @@ mod tests {
|
||||||
#[case::nar_sha256(
|
#[case::nar_sha256(
|
||||||
Fetch::NAR{
|
Fetch::NAR{
|
||||||
url: Url::parse("https://cache.nixos.org/nar/0r8nqa1klm5v17ifc6z96m9wywxkjvgbnqq9pmy0sgqj53wj3n12.nar.xz").unwrap(),
|
url: Url::parse("https://cache.nixos.org/nar/0r8nqa1klm5v17ifc6z96m9wywxkjvgbnqq9pmy0sgqj53wj3n12.nar.xz").unwrap(),
|
||||||
hash: nixhash::from_sri_str("sha256-oj6yfWKbcEerK8D9GdPJtIAOveNcsH1ztGeSARGypRA=").unwrap(),
|
hash: NixHash::from_sri("sha256-oj6yfWKbcEerK8D9GdPJtIAOveNcsH1ztGeSARGypRA=").unwrap(),
|
||||||
},
|
},
|
||||||
Some(StorePathRef::from_bytes(b"b40vjphshq4fdgv8s3yrp0bdlafi4920-0r8nqa1klm5v17ifc6z96m9wywxkjvgbnqq9pmy0sgqj53wj3n12.nar.xz").unwrap()),
|
Some(StorePathRef::from_bytes(b"b40vjphshq4fdgv8s3yrp0bdlafi4920-0r8nqa1klm5v17ifc6z96m9wywxkjvgbnqq9pmy0sgqj53wj3n12.nar.xz").unwrap()),
|
||||||
"0r8nqa1klm5v17ifc6z96m9wywxkjvgbnqq9pmy0sgqj53wj3n12.nar.xz"
|
"0r8nqa1klm5v17ifc6z96m9wywxkjvgbnqq9pmy0sgqj53wj3n12.nar.xz"
|
||||||
|
|
@ -642,7 +642,7 @@ mod tests {
|
||||||
#[case::nar_sha1(
|
#[case::nar_sha1(
|
||||||
Fetch::NAR{
|
Fetch::NAR{
|
||||||
url: Url::parse("https://cache.nixos.org/nar/0r8nqa1klm5v17ifc6z96m9wywxkjvgbnqq9pmy0sgqj53wj3n12.nar.xz").unwrap(),
|
url: Url::parse("https://cache.nixos.org/nar/0r8nqa1klm5v17ifc6z96m9wywxkjvgbnqq9pmy0sgqj53wj3n12.nar.xz").unwrap(),
|
||||||
hash: nixhash::from_sri_str("sha1-F/fMsgwkXF8fPCg1v9zPZ4yOFIA=").unwrap(),
|
hash: NixHash::from_sri("sha1-F/fMsgwkXF8fPCg1v9zPZ4yOFIA=").unwrap(),
|
||||||
},
|
},
|
||||||
Some(StorePathRef::from_bytes(b"8kx7fdkdbzs4fkfb57xq0cbhs20ymq2n-0r8nqa1klm5v17ifc6z96m9wywxkjvgbnqq9pmy0sgqj53wj3n12.nar.xz").unwrap()),
|
Some(StorePathRef::from_bytes(b"8kx7fdkdbzs4fkfb57xq0cbhs20ymq2n-0r8nqa1klm5v17ifc6z96m9wywxkjvgbnqq9pmy0sgqj53wj3n12.nar.xz").unwrap()),
|
||||||
"0r8nqa1klm5v17ifc6z96m9wywxkjvgbnqq9pmy0sgqj53wj3n12.nar.xz"
|
"0r8nqa1klm5v17ifc6z96m9wywxkjvgbnqq9pmy0sgqj53wj3n12.nar.xz"
|
||||||
|
|
@ -650,7 +650,7 @@ mod tests {
|
||||||
#[case::nar_sha1(
|
#[case::nar_sha1(
|
||||||
Fetch::Executable{
|
Fetch::Executable{
|
||||||
url: Url::parse("https://cache.nixos.org/nar/0r8nqa1klm5v17ifc6z96m9wywxkjvgbnqq9pmy0sgqj53wj3n12.nar.xz").unwrap(),
|
url: Url::parse("https://cache.nixos.org/nar/0r8nqa1klm5v17ifc6z96m9wywxkjvgbnqq9pmy0sgqj53wj3n12.nar.xz").unwrap(),
|
||||||
hash: nixhash::from_sri_str("sha1-NKNeU1csW5YJ4lCeWH3Z/apppNU=").unwrap(),
|
hash: NixHash::from_sri("sha1-NKNeU1csW5YJ4lCeWH3Z/apppNU=").unwrap(),
|
||||||
},
|
},
|
||||||
Some(StorePathRef::from_bytes(b"y92hm2xfk1009hrq0ix80j4m5k4j4w21-0r8nqa1klm5v17ifc6z96m9wywxkjvgbnqq9pmy0sgqj53wj3n12.nar.xz").unwrap()),
|
Some(StorePathRef::from_bytes(b"y92hm2xfk1009hrq0ix80j4m5k4j4w21-0r8nqa1klm5v17ifc6z96m9wywxkjvgbnqq9pmy0sgqj53wj3n12.nar.xz").unwrap()),
|
||||||
"0r8nqa1klm5v17ifc6z96m9wywxkjvgbnqq9pmy0sgqj53wj3n12.nar.xz"
|
"0r8nqa1klm5v17ifc6z96m9wywxkjvgbnqq9pmy0sgqj53wj3n12.nar.xz"
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,7 @@ mod tests {
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
use hex_literal::hex;
|
use hex_literal::hex;
|
||||||
use nix_compat::{derivation::Derivation, nixbase32, nixhash, store_path::StorePath};
|
use nix_compat::{derivation::Derivation, nixbase32, nixhash::NixHash, store_path::StorePath};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use super::KnownPaths;
|
use super::KnownPaths;
|
||||||
|
|
@ -186,7 +186,7 @@ mod tests {
|
||||||
static FETCH_URL: LazyLock<Fetch> = LazyLock::new(|| {
|
static FETCH_URL: LazyLock<Fetch> = LazyLock::new(|| {
|
||||||
Fetch::URL {
|
Fetch::URL {
|
||||||
url: Url::parse("https://raw.githubusercontent.com/aaptel/notmuch-extract-patch/f732a53e12a7c91a06755ebfab2007adc9b3063b/notmuch-extract-patch").unwrap(),
|
url: Url::parse("https://raw.githubusercontent.com/aaptel/notmuch-extract-patch/f732a53e12a7c91a06755ebfab2007adc9b3063b/notmuch-extract-patch").unwrap(),
|
||||||
exp_hash: Some(nixhash::from_sri_str("sha256-Xa1Jbl2Eq5+L0ww+Ph1osA3Z/Dxe/RkN1/dITQCdXFk=").unwrap())
|
exp_hash: Some(NixHash::from_sri("sha256-Xa1Jbl2Eq5+L0ww+Ph1osA3Z/Dxe/RkN1/dITQCdXFk=").unwrap())
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,7 @@ impl Derivation {
|
||||||
Sha256::new_with_prefix(format!(
|
Sha256::new_with_prefix(format!(
|
||||||
"fixed:out:{}{}:{}",
|
"fixed:out:{}{}:{}",
|
||||||
ca_kind_prefix(ca_hash),
|
ca_kind_prefix(ca_hash),
|
||||||
ca_hash.hash().to_nix_hex_string(),
|
ca_hash.hash().to_nix_lowerhex_string(),
|
||||||
out_output
|
out_output
|
||||||
.path
|
.path
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ use thiserror;
|
||||||
use crate::derivation::parse_error::{into_nomerror, ErrorKind, NomError, NomResult};
|
use crate::derivation::parse_error::{into_nomerror, ErrorKind, NomError, NomResult};
|
||||||
use crate::derivation::{write, CAHash, Derivation, Output};
|
use crate::derivation::{write, CAHash, Derivation, Output};
|
||||||
use crate::store_path::{self, StorePath};
|
use crate::store_path::{self, StorePath};
|
||||||
use crate::{aterm, nixhash};
|
use crate::{aterm, nixhash, nixhash::NixHash};
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum Error<I> {
|
pub enum Error<I> {
|
||||||
|
|
@ -84,11 +84,11 @@ fn from_algo_and_mode_and_digest<B: AsRef<[u8]>>(
|
||||||
digest: B,
|
digest: B,
|
||||||
) -> crate::nixhash::NixHashResult<CAHash> {
|
) -> crate::nixhash::NixHashResult<CAHash> {
|
||||||
Ok(match algo_and_mode.strip_prefix("r:") {
|
Ok(match algo_and_mode.strip_prefix("r:") {
|
||||||
Some(algo) => nixhash::CAHash::Nar(nixhash::from_algo_and_digest(
|
Some(algo) => nixhash::CAHash::Nar(NixHash::from_algo_and_digest(
|
||||||
algo.try_into()?,
|
algo.try_into()?,
|
||||||
digest.as_ref(),
|
digest.as_ref(),
|
||||||
)?),
|
)?),
|
||||||
None => nixhash::CAHash::Flat(nixhash::from_algo_and_digest(
|
None => nixhash::CAHash::Flat(NixHash::from_algo_and_digest(
|
||||||
algo_and_mode.try_into()?,
|
algo_and_mode.try_into()?,
|
||||||
digest.as_ref(),
|
digest.as_ref(),
|
||||||
)?),
|
)?),
|
||||||
|
|
|
||||||
|
|
@ -326,7 +326,7 @@ fn output_path_construction() {
|
||||||
Output {
|
Output {
|
||||||
path: None, // will be calculated
|
path: None, // will be calculated
|
||||||
ca_hash: Some(crate::nixhash::CAHash::Nar(
|
ca_hash: Some(crate::nixhash::CAHash::Nar(
|
||||||
crate::nixhash::from_algo_and_digest(
|
crate::nixhash::NixHash::from_algo_and_digest(
|
||||||
crate::nixhash::HashAlgo::Sha256,
|
crate::nixhash::HashAlgo::Sha256,
|
||||||
&data_encoding::HEXLOWER
|
&data_encoding::HEXLOWER
|
||||||
.decode(
|
.decode(
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ impl TryFrom<&str> for HashAlgo {
|
||||||
"sha1" => Ok(Self::Sha1),
|
"sha1" => Ok(Self::Sha1),
|
||||||
"sha256" => Ok(Self::Sha256),
|
"sha256" => Ok(Self::Sha256),
|
||||||
"sha512" => Ok(Self::Sha512),
|
"sha512" => Ok(Self::Sha512),
|
||||||
_ => Err(Error::InvalidAlgo(algo_str.to_string())),
|
_ => Err(Error::InvalidAlgo),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -85,9 +85,9 @@ impl CAHash {
|
||||||
}
|
}
|
||||||
"fixed" => {
|
"fixed" => {
|
||||||
if let Some(s) = s.strip_prefix("r:") {
|
if let Some(s) = s.strip_prefix("r:") {
|
||||||
NixHash::from_nix_nixbase32_str(s).map(CAHash::Nar)
|
NixHash::from_nix_nixbase32(s).map(CAHash::Nar)
|
||||||
} else {
|
} else {
|
||||||
NixHash::from_nix_nixbase32_str(s).map(CAHash::Flat)
|
NixHash::from_nix_nixbase32(s).map(CAHash::Flat)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
@ -218,7 +218,12 @@ impl<'de> Deserialize<'de> for CAHash {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{derivation::CAHash, nixhash};
|
use hex_literal::hex;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
derivation::CAHash,
|
||||||
|
nixhash::{HashAlgo, NixHash},
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serialize_flat() {
|
fn serialize_flat() {
|
||||||
|
|
@ -227,8 +232,9 @@ mod tests {
|
||||||
"hashAlgo": "sha256"
|
"hashAlgo": "sha256"
|
||||||
}"#;
|
}"#;
|
||||||
let hash = CAHash::Flat(
|
let hash = CAHash::Flat(
|
||||||
nixhash::from_nix_str(
|
NixHash::from_algo_and_digest(
|
||||||
"sha256:08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba",
|
HashAlgo::Sha256,
|
||||||
|
&hex!("08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba"),
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
|
|
@ -243,8 +249,9 @@ mod tests {
|
||||||
"hashAlgo": "r:sha256"
|
"hashAlgo": "r:sha256"
|
||||||
}"#;
|
}"#;
|
||||||
let hash = CAHash::Nar(
|
let hash = CAHash::Nar(
|
||||||
nixhash::from_nix_str(
|
NixHash::from_algo_and_digest(
|
||||||
"sha256:08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba",
|
HashAlgo::Sha256,
|
||||||
|
&hex!("08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba"),
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
|
|
@ -264,8 +271,9 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
hash,
|
hash,
|
||||||
CAHash::Flat(
|
CAHash::Flat(
|
||||||
nixhash::from_nix_str(
|
NixHash::from_algo_and_digest(
|
||||||
"sha256:08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba"
|
HashAlgo::Sha256,
|
||||||
|
&hex!("08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba")
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
)
|
)
|
||||||
|
|
@ -284,8 +292,9 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
hash,
|
hash,
|
||||||
CAHash::Nar(
|
CAHash::Nar(
|
||||||
nixhash::from_nix_str(
|
NixHash::from_algo_and_digest(
|
||||||
"sha256:08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba"
|
HashAlgo::Sha256,
|
||||||
|
&hex!("08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba")
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
)
|
)
|
||||||
|
|
@ -304,8 +313,9 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
hash,
|
hash,
|
||||||
CAHash::Nar(
|
CAHash::Nar(
|
||||||
nixhash::from_nix_str(
|
NixHash::from_algo_and_digest(
|
||||||
"sha256:08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba"
|
HashAlgo::Sha256,
|
||||||
|
&hex!("08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba"),
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
)
|
)
|
||||||
|
|
@ -324,8 +334,9 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
hash,
|
hash,
|
||||||
CAHash::Nar(
|
CAHash::Nar(
|
||||||
nixhash::from_nix_str(
|
NixHash::from_algo_and_digest(
|
||||||
"sha256:08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba"
|
HashAlgo::Sha256,
|
||||||
|
&hex!("08813cbee9903c62be4c5027726a418a300da4500b2d369d3af9286f4815ceba"),
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,44 @@ pub use algos::HashAlgo;
|
||||||
pub use ca_hash::CAHash;
|
pub use ca_hash::CAHash;
|
||||||
pub use ca_hash::HashMode as CAHashMode;
|
pub use ca_hash::HashMode as CAHashMode;
|
||||||
|
|
||||||
/// NixHash represents hashes known by Nix.
|
/// NixHash represents hashes known by Nix (md5/sha1/sha256/sha512).
|
||||||
|
///
|
||||||
|
/// Internally, these are represented as an enum of 4 kinds (the latter being
|
||||||
|
/// boxed for size reasons, as we rarely use sha512, having a pointer there
|
||||||
|
/// is fine).
|
||||||
|
///
|
||||||
|
/// There's [Self::algo] and [Self::digest_as_bytes] accessors,
|
||||||
|
/// as well as a [Self::from_algo_and_digest] constructor.
|
||||||
|
///
|
||||||
|
/// A few methods to parse (`from_$format_$encoding`) and emit
|
||||||
|
/// (`to_$format_$encoding`) various formats and encodings Nix uses.
|
||||||
|
///
|
||||||
|
/// # Formats
|
||||||
|
/// The following formats exist:
|
||||||
|
///
|
||||||
|
/// ## Nix Format
|
||||||
|
/// Lowercase algo, followed by a colon, then the digest.
|
||||||
|
///
|
||||||
|
/// ## SRI Format
|
||||||
|
/// Uses the lowercase algo, followed by a `-`, then the digest (base64-encoded).
|
||||||
|
/// This is also used in the Display implementation.
|
||||||
|
///
|
||||||
|
/// Contrary to the SRI spec, Nix doesn't have an understanding of passing
|
||||||
|
/// multiple hashes (with different algos) in SRI hashes.
|
||||||
|
/// It instead simply cuts everything off after the expected length for the
|
||||||
|
/// specified algo, and tries to parse the rest in permissive base64 (allowing
|
||||||
|
/// missing padding).
|
||||||
|
///
|
||||||
|
/// ## Digest only
|
||||||
|
/// It's possible to not specify the algo at all. In that case, the expected
|
||||||
|
/// NixHash algo MUST be provided externally.
|
||||||
|
///
|
||||||
|
/// # Encodings
|
||||||
|
/// For "Nix" and "Digest only" formats, the following encodings are supported:
|
||||||
|
///
|
||||||
|
/// - lowerhex,
|
||||||
|
/// - nixbase32,
|
||||||
|
/// - base64 (StdEncoding)
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum NixHash {
|
pub enum NixHash {
|
||||||
Md5([u8; 16]),
|
Md5([u8; 16]),
|
||||||
|
|
@ -72,62 +109,6 @@ impl NixHash {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a new [NixHash] from the Nix default hash format,
|
|
||||||
/// the inverse of [Self::to_nix_nixbase32_string].
|
|
||||||
pub fn from_nix_nixbase32_str(s: &str) -> Option<Self> {
|
|
||||||
let (tag, digest) = s.split_once(':')?;
|
|
||||||
|
|
||||||
(match tag {
|
|
||||||
"md5" => nixbase32::decode_fixed(digest).map(NixHash::Md5),
|
|
||||||
"sha1" => nixbase32::decode_fixed(digest).map(NixHash::Sha1),
|
|
||||||
"sha256" => nixbase32::decode_fixed(digest).map(NixHash::Sha256),
|
|
||||||
"sha512" => nixbase32::decode_fixed(digest)
|
|
||||||
.map(Box::new)
|
|
||||||
.map(NixHash::Sha512),
|
|
||||||
_ => return None,
|
|
||||||
})
|
|
||||||
.ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Formats a [NixHash] in the Nix default hash format,
|
|
||||||
/// which is the algo, followed by a colon, then the lower hex encoded digest.
|
|
||||||
pub fn to_nix_hex_string(&self) -> String {
|
|
||||||
format!(
|
|
||||||
"{}:{}",
|
|
||||||
self.algo(),
|
|
||||||
HEXLOWER.encode(self.digest_as_bytes())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Formats a [NixHash] in the format that's used inside CAHash,
|
|
||||||
/// which is the algo, followed by a colon, then the nixbase32-encoded digest.
|
|
||||||
pub fn to_nix_nixbase32_string(&self) -> String {
|
|
||||||
format!(
|
|
||||||
"{}:{}",
|
|
||||||
self.algo(),
|
|
||||||
nixbase32::encode(self.digest_as_bytes())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Writes a [NixHash] in SRI format to a [std::fmt::Write].
|
|
||||||
pub fn write_sri_str(&self, w: &mut impl std::fmt::Write) -> Result<(), std::fmt::Error> {
|
|
||||||
write!(
|
|
||||||
w,
|
|
||||||
"{}-{}",
|
|
||||||
self.algo(),
|
|
||||||
BASE64.encode(self.digest_as_bytes())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Formats a [NixHash] to an SRI string.
|
|
||||||
pub fn to_sri_string(&self) -> String {
|
|
||||||
let mut s = String::new();
|
|
||||||
self.write_sri_str(&mut s).unwrap();
|
|
||||||
|
|
||||||
s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs a new [NixHash] by specifying [HashAlgo] and digest.
|
/// Constructs a new [NixHash] by specifying [HashAlgo] and digest.
|
||||||
/// It can fail if the passed digest length doesn't match what's expected for
|
/// It can fail if the passed digest length doesn't match what's expected for
|
||||||
/// the passed algo.
|
/// the passed algo.
|
||||||
|
|
@ -144,115 +125,37 @@ pub fn from_algo_and_digest(algo: HashAlgo, digest: &[u8]) -> NixHashResult<NixH
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Errors related to NixHash construction.
|
/// Constructs a new [NixHash] from the Nix default hash format,
|
||||||
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
|
/// the inverse of [Self::to_nix_nixbase32].
|
||||||
pub enum Error {
|
pub fn from_nix_nixbase32(s: &str) -> Option<Self> {
|
||||||
#[error("invalid hash algo: {0}")]
|
let (tag, digest) = s.split_once(':')?;
|
||||||
InvalidAlgo(String),
|
|
||||||
#[error("invalid SRI string: {0}")]
|
(match tag {
|
||||||
InvalidSRI(String),
|
"md5" => nixbase32::decode_fixed(digest).map(NixHash::Md5),
|
||||||
#[error("invalid encoded digest length '{0}' for algo {1}")]
|
"sha1" => nixbase32::decode_fixed(digest).map(NixHash::Sha1),
|
||||||
InvalidDigestLength(usize, HashAlgo),
|
"sha256" => nixbase32::decode_fixed(digest).map(NixHash::Sha256),
|
||||||
#[error("invalid base16 encoding: {0}")]
|
"sha512" => nixbase32::decode_fixed(digest)
|
||||||
InvalidBase16Encoding(data_encoding::DecodeError),
|
.map(Box::new)
|
||||||
#[error("invalid base32 encoding: {0}")]
|
.map(NixHash::Sha512),
|
||||||
InvalidBase32Encoding(data_encoding::DecodeError),
|
_ => return None,
|
||||||
#[error("invalid base64 encoding: {0}")]
|
})
|
||||||
InvalidBase64Encoding(data_encoding::DecodeError),
|
.ok()
|
||||||
#[error("conflicting hash algo: {0} (hash_algo) vs {1} (inline)")]
|
|
||||||
ConflictingHashAlgos(HashAlgo, HashAlgo),
|
|
||||||
#[error("missing inline hash algo, but no externally-specified algo: {0}")]
|
|
||||||
MissingInlineHashAlgo(String),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Nix allows specifying hashes in various encodings, and magically just
|
/// Formats a [NixHash] in the Nix nixbase32 format.
|
||||||
/// derives the encoding.
|
pub fn to_nix_nixbase32(&self) -> String {
|
||||||
/// This function parses strings to a NixHash.
|
format!(
|
||||||
///
|
"{}:{}",
|
||||||
/// Hashes can be:
|
self.algo(),
|
||||||
/// - Nix hash strings
|
nixbase32::encode(self.digest_as_bytes())
|
||||||
/// - SRI hashes
|
)
|
||||||
/// - bare digests
|
|
||||||
///
|
|
||||||
/// Encoding for Nix hash strings or bare digests can be:
|
|
||||||
/// - base16 (lowerhex),
|
|
||||||
/// - nixbase32,
|
|
||||||
/// - base64 (StdEncoding)
|
|
||||||
/// - sri string
|
|
||||||
///
|
|
||||||
/// The encoding is derived from the length of the string and the hash type.
|
|
||||||
/// The hash is communicated out-of-band, but might also be in-band (in the
|
|
||||||
/// case of a nix hash string or SRI), in which it needs to be consistent with the
|
|
||||||
/// one communicated out-of-band.
|
|
||||||
pub fn from_str(s: &str, algo_str: Option<&str>) -> NixHashResult<NixHash> {
|
|
||||||
// if algo_str is some, parse or bail out
|
|
||||||
let algo: Option<HashAlgo> = algo_str.map(HashAlgo::try_from).transpose()?;
|
|
||||||
|
|
||||||
// Peek at the beginning of the string to detect SRI hashes.
|
|
||||||
if s.starts_with("sha1-")
|
|
||||||
|| s.starts_with("sha256-")
|
|
||||||
|| s.starts_with("sha512-")
|
|
||||||
|| s.starts_with("md5-")
|
|
||||||
{
|
|
||||||
let parsed_nixhash = from_sri_str(s)?;
|
|
||||||
|
|
||||||
// ensure the algo matches with what has been passed externally, if so.
|
|
||||||
if let Some(algo) = algo {
|
|
||||||
if algo != parsed_nixhash.algo() {
|
|
||||||
return Err(Error::ConflictingHashAlgos(algo, parsed_nixhash.algo()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Ok(parsed_nixhash);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Peek at the beginning again to see if it's a Nix Hash
|
|
||||||
if s.starts_with("sha1:")
|
|
||||||
|| s.starts_with("sha256:")
|
|
||||||
|| s.starts_with("sha512:")
|
|
||||||
|| s.starts_with("md5:")
|
|
||||||
{
|
|
||||||
let parsed_nixhash = from_nix_str(s)?;
|
|
||||||
|
|
||||||
// ensure the algo matches with what has been passed externally, if so.
|
|
||||||
if let Some(algo) = algo {
|
|
||||||
if algo != parsed_nixhash.algo() {
|
|
||||||
return Err(Error::ConflictingHashAlgos(algo, parsed_nixhash.algo()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Ok(parsed_nixhash);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Neither of these, assume a bare digest, so there MUST be an externally-passed algo.
|
|
||||||
let algo = algo.ok_or_else(|| Error::MissingInlineHashAlgo(s.to_string()))?;
|
|
||||||
decode_digest(s.as_bytes(), algo)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parses a Nix hash string ($algo:$digest) to a NixHash.
|
|
||||||
pub fn from_nix_str(s: &str) -> NixHashResult<NixHash> {
|
|
||||||
if let Some(rest) = s.strip_prefix("sha1:") {
|
|
||||||
decode_digest(rest.as_bytes(), HashAlgo::Sha1)
|
|
||||||
} else if let Some(rest) = s.strip_prefix("sha256:") {
|
|
||||||
decode_digest(rest.as_bytes(), HashAlgo::Sha256)
|
|
||||||
} else if let Some(rest) = s.strip_prefix("sha512:") {
|
|
||||||
decode_digest(rest.as_bytes(), HashAlgo::Sha512)
|
|
||||||
} else if let Some(rest) = s.strip_prefix("md5:") {
|
|
||||||
decode_digest(rest.as_bytes(), HashAlgo::Md5)
|
|
||||||
} else {
|
|
||||||
Err(Error::InvalidAlgo(s.to_string()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses a Nix SRI string to a NixHash.
|
/// Parses a Nix SRI string to a NixHash.
|
||||||
/// Contrary to the SRI spec, Nix doesn't have an understanding of passing
|
/// (See caveats in [Self] on the deviations from the SRI spec)
|
||||||
/// multiple hashes (with different algos) in SRI hashes.
|
pub fn from_sri(s: &str) -> NixHashResult<NixHash> {
|
||||||
/// It instead simply cuts everything off after the expected length for the
|
|
||||||
/// specified algo, and tries to parse the rest in permissive base64 (allowing
|
|
||||||
/// missing padding).
|
|
||||||
pub fn from_sri_str(s: &str) -> NixHashResult<NixHash> {
|
|
||||||
// split at the first occurence of "-"
|
// split at the first occurence of "-"
|
||||||
let (algo_str, digest_str) = s
|
let (algo_str, digest_str) = s.split_once('-').ok_or(Error::InvalidSRI)?;
|
||||||
.split_once('-')
|
|
||||||
.ok_or_else(|| Error::InvalidSRI(s.to_string()))?;
|
|
||||||
|
|
||||||
// try to map the part before that `-` to a supported hash algo:
|
// try to map the part before that `-` to a supported hash algo:
|
||||||
let algo: HashAlgo = algo_str.try_into()?;
|
let algo: HashAlgo = algo_str.try_into()?;
|
||||||
|
|
@ -286,7 +189,103 @@ pub fn from_sri_str(s: &str) -> NixHashResult<NixHash> {
|
||||||
.decode(digest_str.trim_end_with(|c| c == '='))
|
.decode(digest_str.trim_end_with(|c| c == '='))
|
||||||
.map_err(Error::InvalidBase64Encoding)?;
|
.map_err(Error::InvalidBase64Encoding)?;
|
||||||
|
|
||||||
from_algo_and_digest(algo, &digest)
|
Self::from_algo_and_digest(algo, &digest)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes a [NixHash] in SRI format to a [std::fmt::Write].
|
||||||
|
pub fn write_sri_str(&self, w: &mut impl std::fmt::Write) -> Result<(), std::fmt::Error> {
|
||||||
|
write!(
|
||||||
|
w,
|
||||||
|
"{}-{}",
|
||||||
|
self.algo(),
|
||||||
|
BASE64.encode(self.digest_as_bytes())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Formats a [NixHash] to an SRI string.
|
||||||
|
pub fn to_sri_string(&self) -> String {
|
||||||
|
let mut s = String::new();
|
||||||
|
self.write_sri_str(&mut s).unwrap();
|
||||||
|
|
||||||
|
s
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Formats a [NixHash] in the Nix lowerhex format.
|
||||||
|
pub fn to_nix_lowerhex_string(&self) -> String {
|
||||||
|
format!(
|
||||||
|
"{}:{}",
|
||||||
|
self.algo(),
|
||||||
|
HEXLOWER.encode(self.digest_as_bytes())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This parses all known output formats for NixHash.
|
||||||
|
/// See [NixHash] for a list.
|
||||||
|
/// An optional algo needs to be provided, which is mandatory to be specified if
|
||||||
|
/// the "digest only" format is used.
|
||||||
|
/// In other cases, consistency of an optionally externally configured algo
|
||||||
|
/// with the one parsed is ensured.
|
||||||
|
pub fn from_str(s: &str, want_algo: Option<HashAlgo>) -> NixHashResult<NixHash> {
|
||||||
|
// Check for SRI hashes.
|
||||||
|
if let Ok(parsed_nixhash) = Self::from_sri(s) {
|
||||||
|
// ensure the algo matches with what has been passed externally, if so.
|
||||||
|
if let Some(algo) = want_algo {
|
||||||
|
if algo != parsed_nixhash.algo() {
|
||||||
|
return Err(Error::ConflictingHashAlgos(algo, parsed_nixhash.algo()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(parsed_nixhash);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for $algo:$digest style NixHash.
|
||||||
|
if let Some(parsed_nixhash) = {
|
||||||
|
if let Some(rest) = s.strip_prefix("sha1:") {
|
||||||
|
Some(decode_digest(rest.as_bytes(), HashAlgo::Sha1)?)
|
||||||
|
} else if let Some(rest) = s.strip_prefix("sha256:") {
|
||||||
|
Some(decode_digest(rest.as_bytes(), HashAlgo::Sha256)?)
|
||||||
|
} else if let Some(rest) = s.strip_prefix("sha512:") {
|
||||||
|
Some(decode_digest(rest.as_bytes(), HashAlgo::Sha512)?)
|
||||||
|
} else if let Some(rest) = s.strip_prefix("md5:") {
|
||||||
|
Some(decode_digest(rest.as_bytes(), HashAlgo::Md5)?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} {
|
||||||
|
// ensure the algo matches with what has been passed externally, if so.
|
||||||
|
if let Some(algo) = want_algo {
|
||||||
|
if algo != parsed_nixhash.algo() {
|
||||||
|
return Err(Error::ConflictingHashAlgos(algo, parsed_nixhash.algo()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(parsed_nixhash);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're left with the bare digest case, so there MUST be an externally-passed algo.
|
||||||
|
let algo = want_algo.ok_or_else(|| Error::MissingInlineHashAlgo(s.to_string()))?;
|
||||||
|
decode_digest(s.as_bytes(), algo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Errors related to NixHash construction.
|
||||||
|
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("invalid hash algo")]
|
||||||
|
InvalidAlgo,
|
||||||
|
#[error("invalid SRI string")]
|
||||||
|
InvalidSRI,
|
||||||
|
#[error("invalid encoded digest length '{0}' for algo {1}")]
|
||||||
|
InvalidDigestLength(usize, HashAlgo),
|
||||||
|
#[error("invalid base16 encoding: {0}")]
|
||||||
|
InvalidBase16Encoding(data_encoding::DecodeError),
|
||||||
|
#[error("invalid base32 encoding: {0}")]
|
||||||
|
InvalidBase32Encoding(data_encoding::DecodeError),
|
||||||
|
#[error("invalid base64 encoding: {0}")]
|
||||||
|
InvalidBase64Encoding(data_encoding::DecodeError),
|
||||||
|
#[error("conflicting hash algo: {0} (hash_algo) vs {1} (inline)")]
|
||||||
|
ConflictingHashAlgos(HashAlgo, HashAlgo),
|
||||||
|
#[error("missing inline hash algo, but no externally-specified algo: {0:?}")]
|
||||||
|
MissingInlineHashAlgo(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decode a plain digest depending on the hash algo specified externally.
|
/// Decode a plain digest depending on the hash algo specified externally.
|
||||||
|
|
@ -309,14 +308,14 @@ fn decode_digest(s: &[u8], algo: HashAlgo) -> NixHashResult<NixHash> {
|
||||||
Err(Error::InvalidDigestLength(s.len(), algo))?
|
Err(Error::InvalidDigestLength(s.len(), algo))?
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(from_algo_and_digest(algo, &digest).unwrap())
|
Ok(NixHash::from_algo_and_digest(algo, &digest).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
nixbase32,
|
nixbase32,
|
||||||
nixhash::{self, HashAlgo, NixHash},
|
nixhash::{HashAlgo, NixHash},
|
||||||
};
|
};
|
||||||
use data_encoding::{BASE64, BASE64_NOPAD, HEXLOWER};
|
use data_encoding::{BASE64, BASE64_NOPAD, HEXLOWER};
|
||||||
use hex_literal::hex;
|
use hex_literal::hex;
|
||||||
|
|
@ -345,68 +344,67 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
fn make_nixhash(algo: &HashAlgo, digest_encoded: String) -> String {
|
fn make_nixhash(algo: HashAlgo, digest_encoded: String) -> String {
|
||||||
format!("{}:{}", algo, digest_encoded)
|
format!("{}:{}", algo, digest_encoded)
|
||||||
}
|
}
|
||||||
fn make_sri_string(algo: &HashAlgo, digest_encoded: String) -> String {
|
fn make_sri_string(algo: HashAlgo, digest_encoded: String) -> String {
|
||||||
format!("{}-{}", algo, digest_encoded)
|
format!("{}-{}", algo, digest_encoded)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test parsing a hash string in various formats, and also when/how the out-of-band algo is needed.
|
/// Test parsing a hash string in various formats, and also when/how the out-of-band algo is needed.
|
||||||
#[rstest]
|
#[rstest]
|
||||||
#[case::sha1(&NixHash::Sha1(DIGEST_SHA1))]
|
#[case::sha1(NixHash::Sha1(DIGEST_SHA1))]
|
||||||
#[case::sha256(&NixHash::Sha256(DIGEST_SHA256))]
|
#[case::sha256(NixHash::Sha256(DIGEST_SHA256))]
|
||||||
#[case::sha512(&NixHash::Sha512(Box::new(DIGEST_SHA512)))]
|
#[case::sha512(NixHash::Sha512(Box::new(DIGEST_SHA512)))]
|
||||||
#[case::md5(&NixHash::Md5(DIGEST_MD5))]
|
#[case::md5(NixHash::Md5(DIGEST_MD5))]
|
||||||
fn from_str(#[case] expected_hash: &NixHash) {
|
fn from_str(#[case] expected_hash: NixHash) {
|
||||||
let algo = &expected_hash.algo();
|
let algo = expected_hash.algo();
|
||||||
let digest = expected_hash.digest_as_bytes();
|
let digest = expected_hash.digest_as_bytes();
|
||||||
// parse SRI
|
// parse SRI
|
||||||
{
|
{
|
||||||
// base64 without out-of-band algo
|
// base64 without out-of-band algo
|
||||||
let s = make_sri_string(algo, to_base64(digest));
|
let s = make_sri_string(algo, to_base64(digest));
|
||||||
let h = nixhash::from_str(&s, None).expect("must succeed");
|
let h = NixHash::from_str(&s, None).expect("must succeed");
|
||||||
assert_eq!(expected_hash, &h);
|
assert_eq!(expected_hash, h);
|
||||||
|
|
||||||
// base64 with out-of-band-algo
|
// base64 with out-of-band-algo
|
||||||
let s = make_sri_string(algo, to_base64(digest));
|
let s = make_sri_string(algo, to_base64(digest));
|
||||||
let h = nixhash::from_str(&s, Some(&expected_hash.algo().to_string()))
|
let h = NixHash::from_str(&s, Some(expected_hash.algo())).expect("must succeed");
|
||||||
.expect("must succeed");
|
assert_eq!(expected_hash, h);
|
||||||
assert_eq!(expected_hash, &h);
|
|
||||||
|
|
||||||
// base64_nopad without out-of-band algo
|
// base64_nopad without out-of-band algo
|
||||||
let s = make_sri_string(algo, to_base64_nopad(digest));
|
let s = make_sri_string(algo, to_base64_nopad(digest));
|
||||||
let h = nixhash::from_str(&s, None).expect("must succeed");
|
let h = NixHash::from_str(&s, None).expect("must succeed");
|
||||||
assert_eq!(expected_hash, &h);
|
assert_eq!(expected_hash, h);
|
||||||
|
|
||||||
// base64_nopad with out-of-band-algo
|
// base64_nopad with out-of-band-algo
|
||||||
let s = make_sri_string(algo, to_base64_nopad(digest));
|
let s = make_sri_string(algo, to_base64_nopad(digest));
|
||||||
let h = nixhash::from_str(&s, Some(&algo.to_string())).expect("must succeed");
|
let h = NixHash::from_str(&s, Some(algo)).expect("must succeed");
|
||||||
assert_eq!(expected_hash, &h);
|
assert_eq!(expected_hash, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse plain base16. should succeed with algo out-of-band, but fail without.
|
// parse plain base16. should succeed with algo out-of-band, but fail without.
|
||||||
{
|
{
|
||||||
let s = to_base16(digest);
|
let s = to_base16(digest);
|
||||||
nixhash::from_str(&s, None).expect_err("must fail");
|
NixHash::from_str(&s, None).expect_err("must fail");
|
||||||
let h = nixhash::from_str(&s, Some(&algo.to_string())).expect("must succeed");
|
let h = NixHash::from_str(&s, Some(algo)).expect("must succeed");
|
||||||
assert_eq!(expected_hash, &h);
|
assert_eq!(expected_hash, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse plain nixbase32. should succeed with algo out-of-band, but fail without.
|
// parse plain nixbase32. should succeed with algo out-of-band, but fail without.
|
||||||
{
|
{
|
||||||
let s = to_nixbase32(digest);
|
let s = to_nixbase32(digest);
|
||||||
nixhash::from_str(&s, None).expect_err("must fail");
|
NixHash::from_str(&s, None).expect_err("must fail");
|
||||||
let h = nixhash::from_str(&s, Some(&algo.to_string())).expect("must succeed");
|
let h = NixHash::from_str(&s, Some(algo)).expect("must succeed");
|
||||||
assert_eq!(expected_hash, &h);
|
assert_eq!(expected_hash, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse plain base64. should succeed with algo out-of-band, but fail without.
|
// parse plain base64. should succeed with algo out-of-band, but fail without.
|
||||||
{
|
{
|
||||||
let s = to_base64(digest);
|
let s = to_base64(digest);
|
||||||
nixhash::from_str(&s, None).expect_err("must fail");
|
NixHash::from_str(&s, None).expect_err("must fail");
|
||||||
let h = nixhash::from_str(&s, Some(&algo.to_string())).expect("must succeed");
|
let h = NixHash::from_str(&s, Some(algo)).expect("must succeed");
|
||||||
assert_eq!(expected_hash, &h);
|
assert_eq!(expected_hash, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse Nix hash strings
|
// parse Nix hash strings
|
||||||
|
|
@ -416,11 +414,11 @@ mod tests {
|
||||||
let s = make_nixhash(algo, to_base16(digest));
|
let s = make_nixhash(algo, to_base16(digest));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expected_hash,
|
expected_hash,
|
||||||
&nixhash::from_str(&s, None).expect("must succeed")
|
NixHash::from_str(&s, None).expect("must succeed")
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expected_hash,
|
expected_hash,
|
||||||
&nixhash::from_str(&s, Some(&algo.to_string())).expect("must succeed")
|
NixHash::from_str(&s, Some(algo)).expect("must succeed")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// nixbase32. should succeed with both algo out-of-band and in-band.
|
// nixbase32. should succeed with both algo out-of-band and in-band.
|
||||||
|
|
@ -428,11 +426,11 @@ mod tests {
|
||||||
let s = make_nixhash(algo, to_nixbase32(digest));
|
let s = make_nixhash(algo, to_nixbase32(digest));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expected_hash,
|
expected_hash,
|
||||||
&nixhash::from_str(&s, None).expect("must succeed")
|
NixHash::from_str(&s, None).expect("must succeed")
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expected_hash,
|
expected_hash,
|
||||||
&nixhash::from_str(&s, Some(&algo.to_string())).expect("must succeed")
|
NixHash::from_str(&s, Some(algo)).expect("must succeed")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// base64. should succeed with both algo out-of-band and in-band.
|
// base64. should succeed with both algo out-of-band and in-band.
|
||||||
|
|
@ -440,11 +438,11 @@ mod tests {
|
||||||
let s = make_nixhash(algo, to_base64(digest));
|
let s = make_nixhash(algo, to_base64(digest));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expected_hash,
|
expected_hash,
|
||||||
&nixhash::from_str(&s, None).expect("must succeed")
|
NixHash::from_str(&s, None).expect("must succeed")
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expected_hash,
|
expected_hash,
|
||||||
&nixhash::from_str(&s, Some(&algo.to_string())).expect("must succeed")
|
NixHash::from_str(&s, Some(algo)).expect("must succeed")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -453,7 +451,7 @@ mod tests {
|
||||||
/// Test parsing an SRI hash via the [nixhash::from_sri_str] method.
|
/// Test parsing an SRI hash via the [nixhash::from_sri_str] method.
|
||||||
#[test]
|
#[test]
|
||||||
fn from_sri_str() {
|
fn from_sri_str() {
|
||||||
let nix_hash = nixhash::from_sri_str("sha256-pc6cFV7Qk5dhRkbJcX/HzZSxAj17drYY1Ank/v1unTk=")
|
let nix_hash = NixHash::from_sri("sha256-pc6cFV7Qk5dhRkbJcX/HzZSxAj17drYY1Ank/v1unTk=")
|
||||||
.expect("must succeed");
|
.expect("must succeed");
|
||||||
|
|
||||||
assert_eq!(HashAlgo::Sha256, nix_hash.algo());
|
assert_eq!(HashAlgo::Sha256, nix_hash.algo());
|
||||||
|
|
@ -471,7 +469,7 @@ mod tests {
|
||||||
#[case::too_much_padding("sha512-7g91TBvYoYQorRTqo+rYD/i5YnWvUBLnqDhPHxBJDaBW7smuPMeRp6E6JOFuVN9bzN0QnH1ToUU0u9c2CjALEQ===")]
|
#[case::too_much_padding("sha512-7g91TBvYoYQorRTqo+rYD/i5YnWvUBLnqDhPHxBJDaBW7smuPMeRp6E6JOFuVN9bzN0QnH1ToUU0u9c2CjALEQ===")]
|
||||||
#[case::additional_suffix_ignored("sha512-7g91TBvYoYQorRTqo+rYD/i5YnWvUBLnqDhPHxBJDaBW7smuPMeRp6E6JOFuVN9bzN0QnH1ToUU0u9c2CjALEQ== cheesecake")]
|
#[case::additional_suffix_ignored("sha512-7g91TBvYoYQorRTqo+rYD/i5YnWvUBLnqDhPHxBJDaBW7smuPMeRp6E6JOFuVN9bzN0QnH1ToUU0u9c2CjALEQ== cheesecake")]
|
||||||
fn from_sri_str_sha512_paddings(#[case] sri_str: &str) {
|
fn from_sri_str_sha512_paddings(#[case] sri_str: &str) {
|
||||||
let nix_hash = nixhash::from_sri_str(sri_str).expect("must succeed");
|
let nix_hash = NixHash::from_sri(sri_str).expect("must succeed");
|
||||||
|
|
||||||
assert_eq!(HashAlgo::Sha512, nix_hash.algo());
|
assert_eq!(HashAlgo::Sha512, nix_hash.algo());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
@ -484,14 +482,13 @@ mod tests {
|
||||||
/// doesn't match what's expected from that hash function.
|
/// doesn't match what's expected from that hash function.
|
||||||
#[test]
|
#[test]
|
||||||
fn from_sri_str_truncated() {
|
fn from_sri_str_truncated() {
|
||||||
nixhash::from_sri_str("sha256-pc6cFV7Qk5dhRkbJcX/HzZSxAj17drYY1Ank")
|
NixHash::from_sri("sha256-pc6cFV7Qk5dhRkbJcX/HzZSxAj17drYY1Ank").expect_err("must fail");
|
||||||
.expect_err("must fail");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ensure we fail on SRI hashes that Nix doesn't support.
|
/// Ensure we fail on SRI hashes that Nix doesn't support.
|
||||||
#[test]
|
#[test]
|
||||||
fn from_sri_str_unsupported() {
|
fn from_sri_str_unsupported() {
|
||||||
nixhash::from_sri_str(
|
NixHash::from_sri(
|
||||||
"sha384-o4UVSl89mIB0sFUK+3jQbG+C9Zc9dRlV/Xd3KAvXEbhqxu0J5OAdg6b6VHKHwQ7U",
|
"sha384-o4UVSl89mIB0sFUK+3jQbG+C9Zc9dRlV/Xd3KAvXEbhqxu0J5OAdg6b6VHKHwQ7U",
|
||||||
)
|
)
|
||||||
.expect_err("must fail");
|
.expect_err("must fail");
|
||||||
|
|
@ -500,7 +497,7 @@ mod tests {
|
||||||
/// Ensure we reject invalid base64 encoding
|
/// Ensure we reject invalid base64 encoding
|
||||||
#[test]
|
#[test]
|
||||||
fn from_sri_str_invalid_base64() {
|
fn from_sri_str_invalid_base64() {
|
||||||
nixhash::from_sri_str("sha256-invalid=base64").expect_err("must fail");
|
NixHash::from_sri("sha256-invalid=base64").expect_err("must fail");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Nix also accepts SRI strings with missing padding, but only in case the
|
/// Nix also accepts SRI strings with missing padding, but only in case the
|
||||||
|
|
@ -519,17 +516,20 @@ mod tests {
|
||||||
hex!("7e022bdd3c851830173f9faaa006a230a0e0fdad4c953e85bff4bf0da036e12f");
|
hex!("7e022bdd3c851830173f9faaa006a230a0e0fdad4c953e85bff4bf0da036e12f");
|
||||||
|
|
||||||
// passing hash algo out of band should succeed
|
// passing hash algo out of band should succeed
|
||||||
let nix_hash = nixhash::from_str(&format!("sha256-{}", &broken_base64), Some("sha256"))
|
let nix_hash = NixHash::from_str(
|
||||||
|
&format!("sha256-{}", &broken_base64),
|
||||||
|
Some(HashAlgo::Sha256),
|
||||||
|
)
|
||||||
.expect("must succeed");
|
.expect("must succeed");
|
||||||
assert_eq!(&expected_digest, &nix_hash.digest_as_bytes());
|
assert_eq!(&expected_digest, &nix_hash.digest_as_bytes());
|
||||||
|
|
||||||
// not passing hash algo out of band should succeed
|
// not passing hash algo out of band should succeed
|
||||||
let nix_hash =
|
let nix_hash =
|
||||||
nixhash::from_str(&format!("sha256-{}", &broken_base64), None).expect("must succeed");
|
NixHash::from_str(&format!("sha256-{}", &broken_base64), None).expect("must succeed");
|
||||||
assert_eq!(&expected_digest, &nix_hash.digest_as_bytes());
|
assert_eq!(&expected_digest, &nix_hash.digest_as_bytes());
|
||||||
|
|
||||||
// not passing SRI, but hash algo out of band should fail
|
// not passing SRI, but hash algo out of band should fail
|
||||||
nixhash::from_str(broken_base64, Some("sha256")).expect_err("must fail");
|
NixHash::from_str(broken_base64, Some(HashAlgo::Sha256)).expect_err("must fail");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// As we decided to pass our hashes by trimming `=` completely,
|
/// As we decided to pass our hashes by trimming `=` completely,
|
||||||
|
|
@ -544,17 +544,18 @@ mod tests {
|
||||||
let expected_digest =
|
let expected_digest =
|
||||||
hex!("b3271e24c5049270430872bc786b3aad45372109fe1e741f5117c2ac3c583daf");
|
hex!("b3271e24c5049270430872bc786b3aad45372109fe1e741f5117c2ac3c583daf");
|
||||||
|
|
||||||
let nix_hash = nixhash::from_str(&format!("sha256-{}", &weird_base64), Some("sha256"))
|
let nix_hash =
|
||||||
|
NixHash::from_str(&format!("sha256-{}", &weird_base64), Some(HashAlgo::Sha256))
|
||||||
.expect("must succeed");
|
.expect("must succeed");
|
||||||
assert_eq!(&expected_digest, &nix_hash.digest_as_bytes());
|
assert_eq!(&expected_digest, &nix_hash.digest_as_bytes());
|
||||||
|
|
||||||
// not passing hash algo out of band should succeed
|
// not passing hash algo out of band should succeed
|
||||||
let nix_hash =
|
let nix_hash =
|
||||||
nixhash::from_str(&format!("sha256-{}", &weird_base64), None).expect("must succeed");
|
NixHash::from_str(&format!("sha256-{}", &weird_base64), None).expect("must succeed");
|
||||||
assert_eq!(&expected_digest, &nix_hash.digest_as_bytes());
|
assert_eq!(&expected_digest, &nix_hash.digest_as_bytes());
|
||||||
|
|
||||||
// not passing SRI, but hash algo out of band should fail
|
// not passing SRI, but hash algo out of band should fail
|
||||||
nixhash::from_str(weird_base64, Some("sha256")).expect_err("must fail");
|
NixHash::from_str(weird_base64, Some(HashAlgo::Sha256)).expect_err("must fail");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ impl<'de> Deserialize<'de> for NixHash {
|
||||||
D: serde::Deserializer<'de>,
|
D: serde::Deserializer<'de>,
|
||||||
{
|
{
|
||||||
let str: &'de str = Deserialize::deserialize(deserializer)?;
|
let str: &'de str = Deserialize::deserialize(deserializer)?;
|
||||||
super::from_str(str, None).map_err(|_| {
|
NixHash::from_str(str, None).map_err(|_| {
|
||||||
serde::de::Error::invalid_value(serde::de::Unexpected::Str(str), &"NixHash")
|
serde::de::Error::invalid_value(serde::de::Unexpected::Str(str), &"NixHash")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -27,16 +27,16 @@ impl Serialize for NixHash {
|
||||||
/// The length of a sha256 digest, nixbase32-encoded.
|
/// The length of a sha256 digest, nixbase32-encoded.
|
||||||
const NIXBASE32_SHA256_ENCODE_LEN: usize = nixbase32::encode_len(32);
|
const NIXBASE32_SHA256_ENCODE_LEN: usize = nixbase32::encode_len(32);
|
||||||
|
|
||||||
pub fn from_nix_hash_string<'de, D>(deserializer: D) -> Result<[u8; 32], D::Error>
|
pub fn from_nix_nixbase32_or_sri<'de, D>(deserializer: D) -> Result<[u8; 32], D::Error>
|
||||||
where
|
where
|
||||||
D: serde::Deserializer<'de>,
|
D: serde::Deserializer<'de>,
|
||||||
{
|
{
|
||||||
let str: &'de str = Deserialize::deserialize(deserializer)?;
|
let str: &'de str = Deserialize::deserialize(deserializer)?;
|
||||||
if let Some(digest_str) = str.strip_prefix("sha256:") {
|
if let Some(digest_str) = str.strip_prefix("sha256:") {
|
||||||
return from_nix_nixbase32_string::<D>(digest_str);
|
return from_nix_nixbase32::<D>(digest_str);
|
||||||
}
|
}
|
||||||
if let Some(digest_str) = str.strip_prefix("sha256-") {
|
if let Some(digest_str) = str.strip_prefix("sha256-") {
|
||||||
return from_sri_string::<D>(digest_str);
|
return from_sri::<D>(digest_str);
|
||||||
}
|
}
|
||||||
Err(serde::de::Error::invalid_value(
|
Err(serde::de::Error::invalid_value(
|
||||||
serde::de::Unexpected::Str(str),
|
serde::de::Unexpected::Str(str),
|
||||||
|
|
@ -44,7 +44,7 @@ where
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_sri_string<'de, D>(str: &str) -> Result<[u8; 32], D::Error>
|
pub fn from_sri<'de, D>(str: &str) -> Result<[u8; 32], D::Error>
|
||||||
where
|
where
|
||||||
D: serde::Deserializer<'de>,
|
D: serde::Deserializer<'de>,
|
||||||
{
|
{
|
||||||
|
|
@ -64,7 +64,7 @@ where
|
||||||
Ok(digest)
|
Ok(digest)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_nix_nixbase32_string<'de, D>(str: &str) -> Result<[u8; 32], D::Error>
|
pub fn from_nix_nixbase32<'de, D>(str: &str) -> Result<[u8; 32], D::Error>
|
||||||
where
|
where
|
||||||
D: serde::Deserializer<'de>,
|
D: serde::Deserializer<'de>,
|
||||||
{
|
{
|
||||||
|
|
@ -80,10 +80,10 @@ where
|
||||||
Ok(digest)
|
Ok(digest)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_nix_nixbase32_string<S>(v: &[u8; 32], serializer: S) -> Result<S::Ok, S::Error>
|
pub fn to_nix_nixbase32<S>(v: &[u8; 32], serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
S: serde::Serializer,
|
S: serde::Serializer,
|
||||||
{
|
{
|
||||||
let string = NixHash::Sha256(*v).to_nix_nixbase32_string();
|
let string = NixHash::Sha256(*v).to_nix_nixbase32();
|
||||||
string.serialize(serializer)
|
string.serialize(serializer)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ pub struct ExportedPathInfo<'a> {
|
||||||
|
|
||||||
#[serde(
|
#[serde(
|
||||||
rename = "narHash",
|
rename = "narHash",
|
||||||
serialize_with = "nixhash::serde::to_nix_nixbase32_string",
|
serialize_with = "nixhash::serde::to_nix_nixbase32",
|
||||||
deserialize_with = "nixhash::serde::from_nix_hash_string"
|
deserialize_with = "nixhash::serde::from_nix_nixbase32_or_sri"
|
||||||
)]
|
)]
|
||||||
pub nar_sha256: [u8; 32],
|
pub nar_sha256: [u8; 32],
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ where
|
||||||
|
|
||||||
/// Helper function, used for the non-sha256 [CAHash::Nar] and all [CAHash::Flat].
|
/// Helper function, used for the non-sha256 [CAHash::Nar] and all [CAHash::Flat].
|
||||||
fn fixed_out_digest(prefix: &str, hash: &NixHash) -> [u8; 32] {
|
fn fixed_out_digest(prefix: &str, hash: &NixHash) -> [u8; 32] {
|
||||||
Sha256::new_with_prefix(format!("{}:{}:", prefix, hash.to_nix_hex_string()))
|
Sha256::new_with_prefix(format!("{}:{}:", prefix, hash.to_nix_lowerhex_string()))
|
||||||
.finalize()
|
.finalize()
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue