Skip to main content

quant_governor/
receipt.rs

1//! Exact fallback and degradation receipt types.
2
3use serde::{Deserialize, Serialize};
4
5/// Receipt for exact fallback decisions (compressed -> raw).
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct ExactFallbackReceipt {
8    /// Digest of the raw (uncompressed) representation
9    pub raw_digest: Digest,
10
11    /// Digest of the compressed representation
12    pub compressed_digest: Digest,
13
14    /// Whether compressed representation should be retained
15    pub fallback_retention: bool,
16
17    /// Reason for fallback
18    pub fallback_reason: Option<String>,
19}
20
21impl ExactFallbackReceipt {
22    /// Create a new exact fallback receipt.
23    pub fn new(raw_digest: Digest, compressed_digest: Digest, fallback_retention: bool) -> Self {
24        Self {
25            raw_digest,
26            compressed_digest,
27            fallback_retention,
28            fallback_reason: None,
29        }
30    }
31
32    /// Create with a fallback reason.
33    pub fn with_reason(
34        raw_digest: Digest,
35        compressed_digest: Digest,
36        fallback_retention: bool,
37        reason: impl Into<String>,
38    ) -> Self {
39        Self {
40            raw_digest,
41            compressed_digest,
42            fallback_retention,
43            fallback_reason: Some(reason.into()),
44        }
45    }
46
47    /// Returns bytes saved by using raw instead of compressed.
48    pub fn bytes_saved(&self) -> Option<u64> {
49        // This would be computed from size metadata in a full implementation
50        None
51    }
52}
53
54/// Digest types for content identification.
55#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
56pub struct Digest {
57    /// Algorithm used for digest
58    pub algorithm: DigestAlgorithm,
59
60    /// Hex-encoded digest value
61    pub value: String,
62}
63
64/// Digest algorithm used for content identification.
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
66#[serde(rename_all = "lowercase")]
67pub enum DigestAlgorithm {
68    /// SHA-256 digest
69    Sha256,
70    /// BLAKE3 digest
71    Blake3,
72}
73
74impl Digest {
75    /// Create a new SHA-256 digest.
76    pub fn sha256(value: impl Into<String>) -> Self {
77        Self {
78            algorithm: DigestAlgorithm::Sha256,
79            value: value.into(),
80        }
81    }
82
83    /// Create a new BLAKE3 digest.
84    pub fn blake3(value: impl Into<String>) -> Self {
85        Self {
86            algorithm: DigestAlgorithm::Blake3,
87            value: value.into(),
88        }
89    }
90}
91
92impl std::fmt::Display for DigestAlgorithm {
93    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94        match self {
95            DigestAlgorithm::Sha256 => write!(f, "sha256"),
96            DigestAlgorithm::Blake3 => write!(f, "blake3"),
97        }
98    }
99}
100
101impl std::fmt::Display for Digest {
102    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103        write!(f, "{}:{}", self.algorithm, self.value)
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110
111    #[test]
112    fn digest_creation() {
113        let d = Digest::sha256("abc123");
114        assert_eq!(d.algorithm, DigestAlgorithm::Sha256);
115        assert_eq!(d.value, "abc123");
116    }
117
118    #[test]
119    fn fallback_receipt_display() {
120        let receipt =
121            ExactFallbackReceipt::new(Digest::sha256("raw"), Digest::blake3("compressed"), true);
122        assert!(receipt.fallback_retention);
123    }
124}