refactor(nix-compat/nixhash): move serde into serde module

Especially the various specific format serializers/deserializers with
used in path_info.rs shouldn't be living there, but in NixHash, so they
can be used by other consumers of the library wanting to restrict to a
certain format.

Change-Id: Id43ba96e3f6ec68999f028854b625d5335d71554
Reviewed-on: https://cl.snix.dev/c/snix/+/30556
Reviewed-by: Ilan Joselevich <personal@ilanjoselevich.com>
Tested-by: besadii
Autosubmit: Florian Klink <flokli@flokli.de>
This commit is contained in:
Florian Klink 2025-06-03 23:34:17 +03:00 committed by clbot
parent 87d80eb1cc
commit 6c1bfd778e
3 changed files with 93 additions and 91 deletions

View file

@ -1,14 +1,13 @@
use crate::nixbase32;
use bstr::ByteSlice;
use data_encoding::{BASE64, BASE64_NOPAD, HEXLOWER};
use serde::Deserialize;
use serde::Serialize;
use std::cmp::Ordering;
use std::fmt::Display;
use thiserror;
mod algos;
mod ca_hash;
pub mod serde;
pub use algos::HashAlgo;
pub use ca_hash::CAHash;
@ -129,28 +128,6 @@ impl NixHash {
}
}
impl<'de> Deserialize<'de> for NixHash {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let str: &'de str = Deserialize::deserialize(deserializer)?;
from_str(str, None).map_err(|_| {
serde::de::Error::invalid_value(serde::de::Unexpected::Str(str), &"NixHash")
})
}
}
impl Serialize for NixHash {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let sri = self.to_sri_string();
sri.serialize(serializer)
}
}
/// Constructs a new [NixHash] by specifying [HashAlgo] and digest.
/// It can fail if the passed digest length doesn't match what's expected for
/// the passed algo.

View file

@ -0,0 +1,89 @@
use super::NixHash;
use crate::nixbase32;
use serde::{Deserialize, Serialize};
impl<'de> Deserialize<'de> for NixHash {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let str: &'de str = Deserialize::deserialize(deserializer)?;
super::from_str(str, None).map_err(|_| {
serde::de::Error::invalid_value(serde::de::Unexpected::Str(str), &"NixHash")
})
}
}
impl Serialize for NixHash {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let sri = self.to_sri_string();
sri.serialize(serializer)
}
}
/// The length of a sha256 digest, nixbase32-encoded.
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>
where
D: serde::Deserializer<'de>,
{
let str: &'de str = Deserialize::deserialize(deserializer)?;
if let Some(digest_str) = str.strip_prefix("sha256:") {
return from_nix_nixbase32_string::<D>(digest_str);
}
if let Some(digest_str) = str.strip_prefix("sha256-") {
return from_sri_string::<D>(digest_str);
}
Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Str(str),
&"extected a valid nixbase32 or sri narHash",
))
}
pub fn from_sri_string<'de, D>(str: &str) -> Result<[u8; 32], D::Error>
where
D: serde::Deserializer<'de>,
{
let digest: [u8; 32] = data_encoding::BASE64
.decode(str.as_bytes())
.map_err(|_| {
serde::de::Error::invalid_value(
serde::de::Unexpected::Str(str),
&"valid base64 encoded string",
)
})?
.try_into()
.map_err(|_| {
serde::de::Error::invalid_value(serde::de::Unexpected::Str(str), &"valid digest len")
})?;
Ok(digest)
}
pub fn from_nix_nixbase32_string<'de, D>(str: &str) -> Result<[u8; 32], D::Error>
where
D: serde::Deserializer<'de>,
{
let digest_str: [u8; NIXBASE32_SHA256_ENCODE_LEN] =
str.as_bytes().try_into().map_err(|_| {
serde::de::Error::invalid_value(serde::de::Unexpected::Str(str), &"valid digest len")
})?;
let digest: [u8; 32] = nixbase32::decode_fixed(digest_str).map_err(|_| {
serde::de::Error::invalid_value(serde::de::Unexpected::Str(str), &"valid nixbase32")
})?;
Ok(digest)
}
pub fn to_nix_nixbase32_string<S>(v: &[u8; 32], serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let string = NixHash::Sha256(*v).to_nix_nixbase32_string();
string.serialize(serializer)
}

View file

@ -1,4 +1,4 @@
use crate::{narinfo::SignatureRef, nixbase32, nixhash::NixHash, store_path::StorePathRef};
use crate::{narinfo::SignatureRef, nixhash, store_path::StorePathRef};
use serde::{Deserialize, Serialize};
use std::collections::BTreeSet;
@ -14,8 +14,8 @@ pub struct ExportedPathInfo<'a> {
#[serde(
rename = "narHash",
serialize_with = "to_nix_nixbase32_string",
deserialize_with = "from_nix_hash_string"
serialize_with = "nixhash::serde::to_nix_nixbase32_string",
deserialize_with = "nixhash::serde::from_nix_hash_string"
)]
pub nar_sha256: [u8; 32],
@ -51,70 +51,6 @@ impl PartialOrd for ExportedPathInfo<'_> {
}
}
fn to_nix_nixbase32_string<S>(v: &[u8; 32], serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let string = NixHash::Sha256(*v).to_nix_nixbase32_string();
string.serialize(serializer)
}
/// The length of a sha256 digest, nixbase32-encoded.
const NIXBASE32_SHA256_ENCODE_LEN: usize = nixbase32::encode_len(32);
fn from_nix_hash_string<'de, D>(deserializer: D) -> Result<[u8; 32], D::Error>
where
D: serde::Deserializer<'de>,
{
let str: &'de str = Deserialize::deserialize(deserializer)?;
if let Some(digest_str) = str.strip_prefix("sha256:") {
return from_nix_nixbase32_string::<D>(digest_str);
}
if let Some(digest_str) = str.strip_prefix("sha256-") {
return from_sri_string::<D>(digest_str);
}
Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Str(str),
&"extected a valid nixbase32 or sri narHash",
))
}
fn from_sri_string<'de, D>(str: &str) -> Result<[u8; 32], D::Error>
where
D: serde::Deserializer<'de>,
{
let digest: [u8; 32] = data_encoding::BASE64
.decode(str.as_bytes())
.map_err(|_| {
serde::de::Error::invalid_value(
serde::de::Unexpected::Str(str),
&"valid base64 encoded string",
)
})?
.try_into()
.map_err(|_| {
serde::de::Error::invalid_value(serde::de::Unexpected::Str(str), &"valid digest len")
})?;
Ok(digest)
}
fn from_nix_nixbase32_string<'de, D>(str: &str) -> Result<[u8; 32], D::Error>
where
D: serde::Deserializer<'de>,
{
let digest_str: [u8; NIXBASE32_SHA256_ENCODE_LEN] =
str.as_bytes().try_into().map_err(|_| {
serde::de::Error::invalid_value(serde::de::Unexpected::Str(str), &"valid digest len")
})?;
let digest: [u8; 32] = nixbase32::decode_fixed(digest_str).map_err(|_| {
serde::de::Error::invalid_value(serde::de::Unexpected::Str(str), &"valid nixbase32")
})?;
Ok(digest)
}
#[cfg(test)]
mod tests {
use hex_literal::hex;