Skip to content

Commit b618857

Browse files
committed
Improve wrong format message with nix hash convert
We have the machinery to make a more informative error, telling the user what format was actually encountered, and not just that it is not the format that was requested.
1 parent 5f42e5e commit b618857

File tree

5 files changed

+43
-22
lines changed

5 files changed

+43
-22
lines changed

src/libutil/hash.cc

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,10 @@ Hash Hash::parseSRI(std::string_view original)
182182
*
183183
* @param resolveAlgo resolves the parsed type (or throws an error when it is not
184184
* possible.)
185+
*
186+
* @return the parsed hash and the format it was parsed from
185187
*/
186-
static Hash parseAnyHelper(std::string_view rest, auto resolveAlgo)
188+
static std::pair<Hash, HashFormat> parseAnyHelper(std::string_view rest, auto resolveAlgo)
187189
{
188190
bool isSRI = false;
189191

@@ -203,34 +205,45 @@ static Hash parseAnyHelper(std::string_view rest, auto resolveAlgo)
203205

204206
HashAlgorithm algo = resolveAlgo(std::move(optParsedAlgo));
205207

206-
auto [decode, formatName] = [&]() -> DecodeNamePair {
208+
auto [decode, formatName, format] = [&]() -> std::tuple<decltype(base16::decode) *, std::string_view, HashFormat> {
207209
if (isSRI) {
208210
/* In the SRI case, we always are using Base64. If the
209211
length is wrong, get an error later. */
210-
return {base64::decode, "SRI"};
212+
return {base64::decode, "SRI", HashFormat::SRI};
211213
} else {
212214
/* Otherwise, decide via the length of the hash (for the
213215
given algorithm) what base encoding it is. */
214-
return baseExplicit(baseFromSize(rest, algo));
216+
auto format = baseFromSize(rest, algo);
217+
auto [decode, formatName] = baseExplicit(format);
218+
return {decode, formatName, format};
215219
}
216220
}();
217221

218-
return parseLowLevel(rest, algo, {decode, formatName});
222+
return {parseLowLevel(rest, algo, {decode, formatName}), format};
219223
}
220224

221225
Hash Hash::parseAnyPrefixed(std::string_view original)
222226
{
223-
return parseAnyHelper(original, [&](std::optional<HashAlgorithm> optParsedAlgo) {
224-
// Either the string or user must provide the type, if they both do they
225-
// must agree.
226-
if (!optParsedAlgo)
227-
throw BadHash("hash '%s' does not include a type", original);
227+
return parseAnyHelper(
228+
original,
229+
[&](std::optional<HashAlgorithm> optParsedAlgo) {
230+
// Either the string or user must provide the type, if they both do they
231+
// must agree.
232+
if (!optParsedAlgo)
233+
throw BadHash("hash '%s' does not include a type", original);
228234

229-
return *optParsedAlgo;
230-
});
235+
return *optParsedAlgo;
236+
})
237+
.first;
231238
}
232239

233240
Hash Hash::parseAny(std::string_view original, std::optional<HashAlgorithm> optAlgo)
241+
{
242+
return parseAnyReturningFormat(original, optAlgo).first;
243+
}
244+
245+
std::pair<Hash, HashFormat>
246+
Hash::parseAnyReturningFormat(std::string_view original, std::optional<HashAlgorithm> optAlgo)
234247
{
235248
return parseAnyHelper(original, [&](std::optional<HashAlgorithm> optParsedAlgo) {
236249
// Either the string or user must provide the type, if they both do they

src/libutil/include/nix/util/hash.hh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@ struct Hash
7979
*/
8080
static Hash parseAny(std::string_view s, std::optional<HashAlgorithm> optAlgo);
8181

82+
/**
83+
* Like `parseAny`, but also returns the format the hash was parsed from.
84+
*/
85+
static std::pair<Hash, HashFormat>
86+
parseAnyReturningFormat(std::string_view s, std::optional<HashAlgorithm> optAlgo);
87+
8288
/**
8389
* Parse a hash from a string representation like the above, except the
8490
* type prefix is mandatory is there is no separate argument.

src/nix/hash-convert.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ R""(
2727

2828
```console
2929
# nix hash convert --hash-algo sha256 --from nix32 ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0=
30-
error: input hash 'ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0=' does not have the expected format '--from nix32'
30+
error: input hash 'ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0=' has format 'base64', but '--from nix32' was specified
3131

3232
# nix hash convert --hash-algo sha256 --from nix32 1b8m03r63zqhnjf7l5wnldhh7c134ap5vpj0850ymkq1iyzicy5s
3333
sha256-ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0=

src/nix/hash.cc

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -248,11 +248,13 @@ struct CmdHashConvert : Command
248248
void run() override
249249
{
250250
for (const auto & s : hashStrings) {
251-
Hash h = from == HashFormat::SRI ? Hash::parseSRI(s) : Hash::parseAny(s, algo);
252-
if (from && from != HashFormat::SRI
253-
&& h.to_string(*from, false) != (from == HashFormat::Base16 ? toLower(s) : s)) {
254-
auto from_as_string = printHashFormat(*from);
255-
throw BadHash("input hash '%s' does not have the expected format for '--from %s'", s, from_as_string);
251+
auto [h, parsedFormat] = Hash::parseAnyReturningFormat(s, algo);
252+
if (from && *from != parsedFormat) {
253+
throw BadHash(
254+
"input hash '%s' has format '%s', but '--from %s' was specified",
255+
s,
256+
printHashFormat(parsedFormat),
257+
printHashFormat(*from));
256258
}
257259
logger->cout(h.to_string(to, to == HashFormat::SRI));
258260
}

tests/functional/hash-convert.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,10 @@ try3() {
9393
# Asserting input format fails.
9494
#
9595

96-
expectStderr 1 nix hash convert --hash-algo "$1" --from sri "$2" | grepQuiet "is not SRI"
97-
expectStderr 1 nix hash convert --hash-algo "$1" --from nix32 "$2" | grepQuiet "input hash"
98-
expectStderr 1 nix hash convert --hash-algo "$1" --from base16 "$3" | grepQuiet "input hash"
99-
expectStderr 1 nix hash convert --hash-algo "$1" --from nix32 "$4" | grepQuiet "input hash"
96+
expectStderr 1 nix hash convert --hash-algo "$1" --from sri "$2" | grepQuiet "'base16', but '--from sri'"
97+
expectStderr 1 nix hash convert --hash-algo "$1" --from nix32 "$2" | grepQuiet "'base16', but '--from nix32'"
98+
expectStderr 1 nix hash convert --hash-algo "$1" --from base16 "$3" | grepQuiet "'nix32', but '--from base16'"
99+
expectStderr 1 nix hash convert --hash-algo "$1" --from nix32 "$4" | grepQuiet "'base64', but '--from nix32'"
100100

101101
# Base-16 hashes can be in uppercase.
102102
nix hash convert --hash-algo "$1" --from base16 "$(echo "$2" | tr '[:lower:]' '[:upper:]')"

0 commit comments

Comments
 (0)