diff options
author | Padraic-O-Mhuiris <patrick.morris.310@gmail.com> | 2024-02-21T16·49+0000 |
---|---|---|
committer | Pádraic Ó Mhuiris <patrick.morris.310@gmail.com> | 2024-02-23T16·04+0000 |
commit | 5c3065b43a61a5fa019cbbb157984fc5eb81d439 (patch) | |
tree | 897a44fdb7da446413276861c13d2a2365ea5f4b /tvix/eval | |
parent | ffb134398dedcae6cd13cdf49b2cd57d43793bda (diff) |
feat(tvix/eval): implement `builtins.hashString` r/7597
Implements md5, sha1, sha256 and sha512 using the related crates from the RustCrypto hashes project (https://github.com/RustCrypto/hashes) Change-Id: I00730dea44ec9ef85309edc27addab0ae88814b8 Reviewed-on: https://cl.tvl.fyi/c/depot/+/11005 Tested-by: BuildkiteCI Reviewed-by: aspen <root@gws.fyi>
Diffstat (limited to 'tvix/eval')
-rw-r--r-- | tvix/eval/Cargo.toml | 4 | ||||
-rw-r--r-- | tvix/eval/docs/builtins.md | 2 | ||||
-rw-r--r-- | tvix/eval/src/builtins/mod.rs | 32 | ||||
-rw-r--r-- | tvix/eval/src/errors.rs | 10 | ||||
-rw-r--r-- | tvix/eval/src/tests/nix_tests/eval-okay-groupBy.exp (renamed from tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-groupBy.exp) | 0 | ||||
-rw-r--r-- | tvix/eval/src/tests/nix_tests/eval-okay-groupBy.nix (renamed from tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-groupBy.nix) | 2 | ||||
-rw-r--r-- | tvix/eval/src/tests/nix_tests/eval-okay-hashstring.exp (renamed from tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-hashstring.exp) | 0 | ||||
-rw-r--r-- | tvix/eval/src/tests/nix_tests/eval-okay-hashstring.nix (renamed from tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-hashstring.nix) | 0 | ||||
-rw-r--r-- | tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hashString.exp | 1 | ||||
-rw-r--r-- | tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hashString.nix | 6 | ||||
-rw-r--r-- | tvix/eval/src/value/string.rs | 6 |
11 files changed, 52 insertions, 11 deletions
diff --git a/tvix/eval/Cargo.toml b/tvix/eval/Cargo.toml index 4f4551a349d2..e2bd07f8279f 100644 --- a/tvix/eval/Cargo.toml +++ b/tvix/eval/Cargo.toml @@ -33,6 +33,10 @@ tabwriter = "1.2" test-strategy = { version = "0.2.1", optional = true } toml = "0.6.0" xml-rs = "0.8.4" +sha2 = "0.10.8" +sha1 = "0.10.6" +md-5 = "0.10.6" +data-encoding = "2.5.0" [dev-dependencies] criterion = "0.5" diff --git a/tvix/eval/docs/builtins.md b/tvix/eval/docs/builtins.md index 39b59437e26b..eff761c7057d 100644 --- a/tvix/eval/docs/builtins.md +++ b/tvix/eval/docs/builtins.md @@ -66,7 +66,7 @@ The `impl` column indicates implementation status in tvix: | hasAttr | false | | | | | hasContext | false | | | | | hashFile | false | | false | todo | -| hashString | false | | | todo | +| hashString | false | | | | | head | false | | | | | import | true | | | | | intersectAttrs | false | | | | diff --git a/tvix/eval/src/builtins/mod.rs b/tvix/eval/src/builtins/mod.rs index 131f2b7bb201..119c0bda2dc3 100644 --- a/tvix/eval/src/builtins/mod.rs +++ b/tvix/eval/src/builtins/mod.rs @@ -5,9 +5,14 @@ use bstr::{ByteSlice, ByteVec}; use builtin_macros::builtins; +use data_encoding::HEXLOWER; use genawaiter::rc::Gen; use imbl::OrdMap; +use md5::Md5; use regex::Regex; +use sha1::Sha1; +use sha2::digest::Output; +use sha2::{Digest, Sha256, Sha512}; use std::cmp::{self, Ordering}; use std::collections::VecDeque; use std::collections::{BTreeMap, HashSet}; @@ -686,15 +691,24 @@ mod pure_builtins { #[builtin("hashString")] #[allow(non_snake_case)] - async fn builtin_hashString( - co: GenCo, - _algo: Value, - _string: Value, - ) -> Result<Value, ErrorKind> { - // FIXME: propagate contexts here. - Ok(Value::from(CatchableErrorKind::UnimplementedFeature( - "hashString".into(), - ))) + async fn builtin_hashString(co: GenCo, algo: Value, s: Value) -> Result<Value, ErrorKind> { + fn hash<D: Digest>(b: &[u8]) -> Output<D> { + let mut hasher = D::new(); + hasher.update(b); + hasher.finalize() + } + + let s = s.to_str()?; + + let encoded_hash = match algo.to_str()?.as_bytes() { + b"md5" => HEXLOWER.encode(hash::<Md5>(&s).as_bstr()), + b"sha1" => HEXLOWER.encode(hash::<Sha1>(&s).as_bstr()), + b"sha256" => HEXLOWER.encode(hash::<Sha256>(&s).as_bstr()), + b"sha512" => HEXLOWER.encode(hash::<Sha512>(&s).as_bstr()), + _ => return Err(ErrorKind::UnknownHashType(s.into())), + }; + + Ok(Value::from(encoded_hash)) } #[builtin("head")] diff --git a/tvix/eval/src/errors.rs b/tvix/eval/src/errors.rs index db02093b8d65..652252dadfa0 100644 --- a/tvix/eval/src/errors.rs +++ b/tvix/eval/src/errors.rs @@ -229,6 +229,10 @@ pub enum ErrorKind { /// tvix-eval when returning a result to the user, never inside of /// eval code. CatchableError(CatchableErrorKind), + + /// Invalid hash type specified, must be one of "md5", "sha1", "sha256" + /// or "sha512" + UnknownHashType(String), } impl error::Error for Error { @@ -533,6 +537,10 @@ to a missing value in the attribute set(s) included via `with`."#, ErrorKind::CatchableError(inner) => { write!(f, "{}", inner) } + + ErrorKind::UnknownHashType(hash_type) => { + write!(f, "unknown hash type '{}'", hash_type) + } } } } @@ -821,6 +829,7 @@ impl Error { | ErrorKind::TvixBug { .. } | ErrorKind::NotImplemented(_) | ErrorKind::WithContext { .. } + | ErrorKind::UnknownHashType(_) | ErrorKind::CatchableError(_) => return None, }; @@ -866,6 +875,7 @@ impl Error { ErrorKind::NotSerialisableToJson(_) => "E036", ErrorKind::UnexpectedContext => "E037", ErrorKind::Utf8 => "E038", + ErrorKind::UnknownHashType(_) => "E039", // Special error code for errors from other Tvix // components. We may want to introduce a code namespacing diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-groupBy.exp b/tvix/eval/src/tests/nix_tests/eval-okay-groupBy.exp index bfca5652a59b..bfca5652a59b 100644 --- a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-groupBy.exp +++ b/tvix/eval/src/tests/nix_tests/eval-okay-groupBy.exp diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-groupBy.nix b/tvix/eval/src/tests/nix_tests/eval-okay-groupBy.nix index 7e0eab28b036..862d89dbd670 100644 --- a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-groupBy.nix +++ b/tvix/eval/src/tests/nix_tests/eval-okay-groupBy.nix @@ -1,4 +1,4 @@ -with import ./../lib.nix; +with import ./lib.nix; builtins.groupBy (n: builtins.substring 0 1 (builtins.hashString "sha256" (toString n)) diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-hashstring.exp b/tvix/eval/src/tests/nix_tests/eval-okay-hashstring.exp index d720a082ddb3..d720a082ddb3 100644 --- a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-hashstring.exp +++ b/tvix/eval/src/tests/nix_tests/eval-okay-hashstring.exp diff --git a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-hashstring.nix b/tvix/eval/src/tests/nix_tests/eval-okay-hashstring.nix index b0f62b245ca8..b0f62b245ca8 100644 --- a/tvix/eval/src/tests/nix_tests/notyetpassing/eval-okay-hashstring.nix +++ b/tvix/eval/src/tests/nix_tests/eval-okay-hashstring.nix diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hashString.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hashString.exp new file mode 100644 index 000000000000..e00b80e561fb --- /dev/null +++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hashString.exp @@ -0,0 +1 @@ +[ "8a0614b4eaa4cffb7515ec101847e198" "8bd218cf61321d8aa05b3602b99f90d2d8cef3d6" "80ac06d74cb6c5d14af718ce8c3c1255969a1a595b76a3cf92354a95331a879a" "0edac513b6b0454705b553deda4c9b055da0939d26d2f73548862817ebeac5378cf64ff7a752ce1a0590a736735d3bbd9e8a7f04d93617cdf514313f5ab5baa4" ] diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hashString.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hashString.nix new file mode 100644 index 000000000000..aed723d3670a --- /dev/null +++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-hashString.nix @@ -0,0 +1,6 @@ +[ + (builtins.hashString "md5" "tvix") + (builtins.hashString "sha1" "tvix") + (builtins.hashString "sha256" "tvix") + (builtins.hashString "sha512" "tvix") +] diff --git a/tvix/eval/src/value/string.rs b/tvix/eval/src/value/string.rs index 2f1592546b85..6ce0d190c0a0 100644 --- a/tvix/eval/src/value/string.rs +++ b/tvix/eval/src/value/string.rs @@ -524,6 +524,12 @@ impl<'a> From<&'a NixString> for &'a BStr { } } +impl From<NixString> for String { + fn from(s: NixString) -> Self { + s.to_string() + } +} + impl From<NixString> for BString { fn from(s: NixString) -> Self { s.as_bstr().to_owned() |