Skip to main content

provenance_mark/
resolution.rs

1use std::{
2    convert::TryFrom,
3    ops::{Range, RangeFrom},
4};
5
6use dcbor::{Date, prelude::*};
7use serde::{Deserialize, Serialize};
8
9use crate::{Error, Result, date::SerializableDate};
10
11// LOW (16 bytes)
12// 0000  0000  0000  00  00
13// 0123  4567  89ab  cd  ef
14// key   hash  id    seq date
15
16// MEDIUM (32 bytes)
17// 00000000  00000000  11111111  1111  1111
18// 01234567  89abcdef  01234567  89ab  cdef
19// key       hash      id        seq   date
20
21// QUARTILE (58 bytes)
22// 0000000000000000  1111111111111111  2222222222222222  3333  333333
23// 0123456789abcdef  0123456789abcdef  0123456789abcdef  0123  456789
24// key               hash              id                seq   date
25
26// HIGH (106 bytes)
27// 00000000000000001111111111111111  22222222222222223333333333333333
28// 44444444444444445555555555555555  6666  666666
29// 0123456789abcdef0123456789abcdef  0123456789abcdef0123456789abcdef
30// 0123456789abcdef0123456789abcdef  0123  456789 key
31// hash                              id                                seq
32// date
33
34#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
35#[repr(u8)]
36#[serde(into = "u8", try_from = "u8")]
37pub enum ProvenanceMarkResolution {
38    Low      = 0,
39    Medium   = 1,
40    Quartile = 2,
41    High     = 3,
42}
43
44impl From<ProvenanceMarkResolution> for u8 {
45    fn from(res: ProvenanceMarkResolution) -> Self { res as u8 }
46}
47
48impl TryFrom<u8> for ProvenanceMarkResolution {
49    type Error = Error;
50
51    fn try_from(value: u8) -> Result<Self> {
52        match value {
53            0 => Ok(ProvenanceMarkResolution::Low),
54            1 => Ok(ProvenanceMarkResolution::Medium),
55            2 => Ok(ProvenanceMarkResolution::Quartile),
56            3 => Ok(ProvenanceMarkResolution::High),
57            _ => Err(Error::ResolutionError {
58                details: format!(
59                    "invalid provenance mark resolution value: {}",
60                    value
61                ),
62            }),
63        }
64    }
65}
66
67impl From<ProvenanceMarkResolution> for CBOR {
68    fn from(res: ProvenanceMarkResolution) -> Self { CBOR::from(res as u8) }
69}
70
71impl TryFrom<CBOR> for ProvenanceMarkResolution {
72    type Error = dcbor::Error;
73
74    fn try_from(cbor: CBOR) -> dcbor::Result<Self> {
75        let value: u8 = cbor.try_into()?;
76        ProvenanceMarkResolution::try_from(value).map_err(dcbor::Error::from)
77    }
78}
79
80type Res = ProvenanceMarkResolution;
81
82impl ProvenanceMarkResolution {
83    pub fn link_length(&self) -> usize {
84        match self {
85            Res::Low => 4,
86            Res::Medium => 8,
87            Res::Quartile => 16,
88            Res::High => 32,
89        }
90    }
91
92    pub fn seq_bytes_length(&self) -> usize {
93        match self {
94            Res::Low => 2,
95            Res::Medium | Res::Quartile | Res::High => 4,
96        }
97    }
98
99    pub fn date_bytes_length(&self) -> usize {
100        match self {
101            Res::Low => 2,
102            Res::Medium => 4,
103            Res::Quartile | Res::High => 6,
104        }
105    }
106
107    pub fn fixed_length(&self) -> usize {
108        self.link_length() * 3
109            + self.seq_bytes_length()
110            + self.date_bytes_length()
111    }
112
113    pub fn key_range(&self) -> Range<usize> { 0..self.link_length() }
114
115    pub fn chain_id_range(&self) -> Range<usize> { 0..self.link_length() }
116
117    pub fn hash_range(&self) -> Range<usize> {
118        self.chain_id_range().end
119            ..self.chain_id_range().end + self.link_length()
120    }
121
122    pub fn seq_bytes_range(&self) -> Range<usize> {
123        self.hash_range().end..self.hash_range().end + self.seq_bytes_length()
124    }
125
126    pub fn date_bytes_range(&self) -> Range<usize> {
127        self.seq_bytes_range().end
128            ..self.seq_bytes_range().end + self.date_bytes_length()
129    }
130
131    pub fn info_range(&self) -> RangeFrom<usize> {
132        self.date_bytes_range().end..
133    }
134
135    /// Serializes a Date into bytes based on the resolution.
136    pub fn serialize_date(&self, date: Date) -> Result<Vec<u8>> {
137        match self {
138            Res::Low => date.serialize_2_bytes().map(|bytes| bytes.to_vec()),
139            Res::Medium => date.serialize_4_bytes().map(|bytes| bytes.to_vec()),
140            Res::Quartile | Res::High => {
141                date.serialize_6_bytes().map(|bytes| bytes.to_vec())
142            }
143        }
144    }
145
146    /// Deserializes bytes into a Date based on the resolution.
147    pub fn deserialize_date(&self, data: &[u8]) -> Result<Date> {
148        match self {
149            Res::Low if data.len() == 2 => {
150                Date::deserialize_2_bytes(&[data[0], data[1]])
151            }
152            Res::Medium if data.len() == 4 => {
153                Date::deserialize_4_bytes(&[data[0], data[1], data[2], data[3]])
154            }
155            Res::Quartile | Res::High if data.len() == 6 => {
156                Date::deserialize_6_bytes(&[
157                    data[0], data[1], data[2], data[3], data[4], data[5],
158                ])
159            }
160            _ => Err(Error::ResolutionError {
161                details: format!(
162                    "invalid date length: expected 2, 4, or 6 bytes, got {}",
163                    data.len()
164                ),
165            }),
166        }
167    }
168
169    /// Serializes a sequence number into bytes based on the resolution.
170    pub fn serialize_seq(&self, seq: u32) -> Result<Vec<u8>> {
171        match self.seq_bytes_length() {
172            2 => {
173                if seq > (u16::MAX as u32) {
174                    return Err(Error::ResolutionError {
175                        details: format!(
176                            "sequence number {} out of range for 2-byte format (max {})",
177                            seq,
178                            u16::MAX
179                        ),
180                    });
181                }
182                Ok((seq as u16).to_be_bytes().to_vec())
183            }
184            4 => Ok(seq.to_be_bytes().to_vec()),
185            _ => unreachable!(),
186        }
187    }
188
189    /// Deserializes bytes into a sequence number based on the resolution.
190    pub fn deserialize_seq(&self, data: &[u8]) -> Result<u32> {
191        match self.seq_bytes_length() {
192            2 if data.len() == 2 => {
193                Ok(u32::from(u16::from_be_bytes([data[0], data[1]])))
194            }
195            4 if data.len() == 4 => {
196                Ok(u32::from_be_bytes([data[0], data[1], data[2], data[3]]))
197            }
198            _ => Err(Error::ResolutionError {
199                details: format!(
200                    "invalid sequence number length: expected 2 or 4 bytes, got {}",
201                    data.len()
202                ),
203            }),
204        }
205    }
206}
207
208impl std::fmt::Display for ProvenanceMarkResolution {
209    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
210        match self {
211            Res::Low => write!(f, "low"),
212            Res::Medium => write!(f, "medium"),
213            Res::Quartile => write!(f, "quartile"),
214            Res::High => write!(f, "high"),
215        }
216    }
217}