Skip to main content

nix_compat/nixhash/
ca_hash.rs

1use crate::nixbase32;
2use crate::nixhash::NixHash;
3use std::borrow::Cow;
4
5/// A Nix CAHash describes a content-addressed hash of a path.
6///
7/// The way Nix prints it as a string is a bit confusing, but there's essentially
8/// three modes, `Flat`, `Nar` and `Text`.
9/// `Flat` and `Nar` support all 4 algos that [NixHash] supports
10/// (sha1, md5, sha256, sha512), `Text` only supports sha256.
11#[derive(Clone, Debug, Eq, PartialEq)]
12pub enum CAHash {
13    Flat(NixHash),  // "fixed flat"
14    Nar(NixHash),   // "fixed recursive"
15    Text([u8; 32]), // "text", only supports sha256
16}
17
18/// Representation for the supported hash modes.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum HashMode {
21    Flat,
22    Nar,
23    Text,
24}
25
26impl CAHash {
27    pub fn hash(&self) -> Cow<'_, NixHash> {
28        match *self {
29            CAHash::Flat(ref digest) => Cow::Borrowed(digest),
30            CAHash::Nar(ref digest) => Cow::Borrowed(digest),
31            CAHash::Text(digest) => Cow::Owned(NixHash::Sha256(digest)),
32        }
33    }
34
35    pub fn mode(&self) -> HashMode {
36        match self {
37            CAHash::Flat(_) => HashMode::Flat,
38            CAHash::Nar(_) => HashMode::Nar,
39            CAHash::Text(_) => HashMode::Text,
40        }
41    }
42
43    /// Constructs a [CAHash] from the textual representation,
44    /// which is one of the three:
45    /// - `text:sha256:$nixbase32sha256digest`
46    /// - `fixed:r:$algo:$nixbase32digest`
47    /// - `fixed:$algo:$nixbase32digest`
48    ///
49    /// These formats are used in NARInfo, for example.
50    pub fn from_nix_hex_str(s: &str) -> Option<Self> {
51        let (tag, s) = s.split_once(':')?;
52
53        match tag {
54            "text" => {
55                let digest = s.strip_prefix("sha256:")?;
56                let digest = nixbase32::decode_fixed(digest).ok()?;
57                Some(CAHash::Text(digest))
58            }
59            "fixed" => {
60                if let Some(s) = s.strip_prefix("r:") {
61                    NixHash::from_nix_nixbase32(s).map(CAHash::Nar)
62                } else {
63                    NixHash::from_nix_nixbase32(s).map(CAHash::Flat)
64                }
65            }
66            _ => None,
67        }
68    }
69}
70
71/// Formats a [CAHash] in the Nix default hash format, which is the format
72/// that's used in NARInfos for example.
73impl std::fmt::Display for CAHash {
74    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75        let (algo, hash) = match self {
76            CAHash::Flat(h) => match h {
77                NixHash::Md5(h) => ("fixed:md5", &h[..]),
78                NixHash::Sha1(h) => ("fixed:sha1", &h[..]),
79                NixHash::Sha256(h) => ("fixed:sha256", &h[..]),
80                NixHash::Sha512(h) => ("fixed:sha512", &h[..]),
81            },
82            CAHash::Nar(h) => match h {
83                NixHash::Md5(h) => ("fixed:r:md5", &h[..]),
84                NixHash::Sha1(h) => ("fixed:r:sha1", &h[..]),
85                NixHash::Sha256(h) => ("fixed:r:sha256", &h[..]),
86                NixHash::Sha512(h) => ("fixed:r:sha512", &h[..]),
87            },
88            CAHash::Text(h) => ("text:sha256", &h[..]),
89        };
90
91        write!(f, "{}:{}", algo, nixbase32::encode(hash))
92    }
93}