From f31ef707f83732c75eaf99e797e25572945953b2 Mon Sep 17 00:00:00 2001 From: Yvan Sraka Date: Mon, 7 Apr 2025 10:59:14 +0200 Subject: [PATCH] feat(castore): implement `FromStr` for trait `B3Digest` This provides a canonical way to parse a `B3Digest`, to be used in, e.g., a CLI like `snix-castore`. Change-Id: If6c7858c1fcd7645721002be7789f5ce2ff97a5c Reviewed-on: https://cl.snix.dev/c/snix/+/30303 Reviewed-by: Florian Klink Tested-by: besadii --- snix/castore/src/digests.rs | 18 ++++++++++++++++++ snix/castore/src/errors.rs | 4 ++++ 2 files changed, 22 insertions(+) diff --git a/snix/castore/src/digests.rs b/snix/castore/src/digests.rs index 9f94c811c..4ce14c26c 100644 --- a/snix/castore/src/digests.rs +++ b/snix/castore/src/digests.rs @@ -1,5 +1,6 @@ use bytes::Bytes; use data_encoding::BASE64; +use std::str::FromStr; use thiserror::Error; #[derive(PartialEq, Eq, Hash)] @@ -15,6 +16,8 @@ impl B3Digest { pub enum Error { #[error("invalid digest length: {0}")] InvalidDigestLen(usize), + #[error("invalid hash type: expected a 'blake3-' prefixed digest")] + InvalidHashType, } impl AsRef<[u8; B3Digest::LENGTH]> for B3Digest { @@ -109,3 +112,18 @@ impl std::fmt::Debug for B3Digest { BASE64.encode_write(&self.0, f) } } + +impl FromStr for B3Digest { + type Err = Error; + + fn from_str(s: &str) -> Result { + if !s.starts_with("blake3-") { + return Err(Error::InvalidHashType); + } + let encoded = &s[7..]; + let decoded = BASE64 + .decode(encoded.as_bytes()) + .map_err(|_| Error::InvalidDigestLen(s.len()))?; + decoded.as_slice().try_into() + } +} diff --git a/snix/castore/src/errors.rs b/snix/castore/src/errors.rs index 23b4822bf..912bf22c3 100644 --- a/snix/castore/src/errors.rs +++ b/snix/castore/src/errors.rs @@ -27,12 +27,16 @@ pub enum ValidateNodeError { /// Invalid symlink target #[error("Invalid symlink target: {0}")] InvalidSymlinkTarget(SymlinkTargetError), + /// Invalid hash type encountered + #[error("invalid hash type: expected a 'blake3-' prefixed digest")] + InvalidHashType, } impl From for ValidateNodeError { fn from(e: crate::digests::Error) -> Self { match e { crate::digests::Error::InvalidDigestLen(n) => ValidateNodeError::InvalidDigestLen(n), + crate::digests::Error::InvalidHashType => ValidateNodeError::InvalidHashType, } } }