1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
//! This crate allows for the computation of cryptographic hashes or arbitrary data structures.
//!
//! It provides a [`Fingerprint`] trait which represents a type whose hash can be computed.
//! It's implemented by default for most common types from [`std`].
//!
//! It relies on traits from the [`digest`] crate, which means its compatible with all
//! [hash implementations](https://github.com/RustCrypto/hashes) from the
//! [Rust Crypto project](https://github.com/RustCrypto/).
//!
//! Hashes are considered stable, changes to how a given data structure is hashed will cause
//! a minor version bump. Note that making a change to your own type definitions might introduce
//! hash collisions. To avoid this, you can include a version number in your data structures.
//!
//! You can include your crate version like this:
//!
//! ```
//! use blake2::Blake2b512;
//! use fingerprint_struct::fingerprint;
//!
//! let payload = "Hello world!";
//! let hash = fingerprint::<Blake2b512>((env!("CARGO_PKG_VERSION"), payload));
//! ```

#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "alloc")]
extern crate alloc;

mod impls;

use digest::{FixedOutput, Output, Update};

/// A data structure whose cryptographic hash can be computed by a hasher.
///
/// Implementations are provided for common [`std`] types, such as primitives, strings, collections
/// and smart pointers. Custom implementations can be easily written manually, or derived
/// automatically using `#[derive(Fingerprint)]`.
pub trait Fingerprint {
    /// Use this value to update a hasher.
    fn fingerprint<U: Update>(&self, hasher: &mut U);
}

/// Calculate the cryptographic hash of a data structure using the default hasher of a given type.
///
/// # Examples
/// ```
/// use sha2::Sha512;
/// use fingerprint_struct::{fingerprint, Fingerprint};
///
/// let hash = fingerprint::<Sha512>("Hello world!");
/// println!("{hash:?}");
/// ```
///
/// ```
/// use blake2::Blake2b512;
/// use fingerprint_struct::{fingerprint, Fingerprint};
///
/// let hash = fingerprint::<Blake2b512>("Hello world!");
/// println!("{hash:?}");
/// ```
pub fn fingerprint<H: Update + FixedOutput + Default>(value: impl Fingerprint) -> Output<H> {
    fingerprint_with(value, H::default())
}

/// Calculate the cryptographic hash of a data structure using provided hasher.
///
/// # Examples
/// ```
/// use blake2::{digest::Digest, Blake2b512};
/// use fingerprint_struct::{fingerprint_with, Fingerprint};
///
/// let hash = fingerprint_with("Hello world!", Blake2b512::new_with_prefix("Application specific prefix"));
/// println!("{hash:?}");
/// ```
pub fn fingerprint_with<H: Update + FixedOutput, T: Fingerprint>(
    value: T,
    mut hasher: H,
) -> Output<H> {
    value.fingerprint(&mut hasher);
    hasher.finalize_fixed()
}

/// Implements the Fingerprint trait for a custom struct or enum.
///
/// Explicit enum discriminants will be used when provided.
///
/// # Examples
/// ```
/// use fingerprint_struct::Fingerprint;
///
/// # #[cfg(feature = "alloc")]
/// #[derive(Fingerprint)]
/// struct SearchResult {
///     title: String,
///     link: String,
///     rating: Option<u8>
/// }
/// ```
///
/// ```
/// use fingerprint_struct::Fingerprint;
///
/// # #[cfg(feature = "alloc")]
/// #[derive(Fingerprint)]
/// enum LoginState {
///     LoggedOut,
///     LoggedIn { token: String }
/// }
/// ```
#[cfg(feature = "derive")]
pub use fingerprint_struct_derive::Fingerprint;