lightswitch_object/
buildid.rs

1use std::fmt;
2use std::fmt::Display;
3use std::fmt::Formatter;
4use std::str;
5
6use anyhow::Result;
7use data_encoding::HEXLOWER;
8use ring::digest::Digest;
9
10/// Represents a build id, which could be either a GNU build ID, the build
11/// ID from Go, or a Sha256 hash of the code in the .text section.
12#[derive(Hash, Eq, PartialEq, Clone, Debug)]
13pub enum BuildId {
14    Gnu(String),
15    Go(String),
16    Sha256(String),
17}
18
19impl BuildId {
20    pub fn gnu_from_bytes(bytes: &[u8]) -> Self {
21        BuildId::Gnu(
22            bytes
23                .iter()
24                .map(|b| format!("{:02x}", b))
25                .collect::<Vec<_>>()
26                .join(""),
27        )
28    }
29
30    pub fn go_from_bytes(bytes: &[u8]) -> Result<Self> {
31        Ok(BuildId::Go(str::from_utf8(bytes)?.to_string()))
32    }
33
34    pub fn sha256_from_digest(digest: &Digest) -> Self {
35        BuildId::Sha256(HEXLOWER.encode(digest.as_ref()))
36    }
37}
38
39impl Display for BuildId {
40    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
41        match self {
42            BuildId::Gnu(build_id) => {
43                write!(f, "gnu-{}", build_id)
44            }
45            BuildId::Go(build_id) => {
46                write!(f, "go-{}", build_id)
47            }
48            BuildId::Sha256(build_id) => {
49                write!(f, "sha256-{}", build_id)
50            }
51        }
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58    use ring::digest::{Context, SHA256};
59
60    #[test]
61    fn test_buildid_constructors() {
62        assert_eq!(
63            BuildId::gnu_from_bytes(&[0xbe, 0xef, 0xca, 0xfe]).to_string(),
64            "gnu-beefcafe"
65        );
66        assert_eq!(
67            BuildId::go_from_bytes("fake".as_bytes())
68                .unwrap()
69                .to_string(),
70            "go-fake"
71        );
72
73        let mut context = Context::new(&SHA256);
74        context.update(&[0xbe, 0xef, 0xca, 0xfe]);
75        let digest = context.finish();
76        assert_eq!(
77            BuildId::sha256_from_digest(&digest).to_string(),
78            "sha256-b80ad5b1508835ca2191ac800f4bb1a5ae1c3e47f13a8f5ed1b1593337ae5af5"
79        );
80    }
81
82    #[test]
83    fn test_buildid_display() {
84        assert_eq!(BuildId::Gnu("fake".into()).to_string(), "gnu-fake");
85        assert_eq!(BuildId::Go("fake".into()).to_string(), "go-fake");
86        assert_eq!(BuildId::Sha256("fake".into()).to_string(), "sha256-fake");
87    }
88}