irontide_core/
info_hashes.rs1use crate::hash::{Id20, Id32};
7use serde::Serialize;
8
9#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
14pub struct InfoHashes {
15 pub v1: Option<Id20>,
17 pub v2: Option<Id32>,
19}
20
21impl InfoHashes {
22 #[must_use]
24 pub fn v1_only(hash: Id20) -> Self {
25 Self {
26 v1: Some(hash),
27 v2: None,
28 }
29 }
30
31 #[must_use]
33 pub fn v2_only(hash: Id32) -> Self {
34 Self {
35 v1: None,
36 v2: Some(hash),
37 }
38 }
39
40 #[must_use]
42 pub fn hybrid(v1: Id20, v2: Id32) -> Self {
43 Self {
44 v1: Some(v1),
45 v2: Some(v2),
46 }
47 }
48
49 #[must_use]
51 pub fn has_v1(&self) -> bool {
52 self.v1.is_some()
53 }
54
55 #[must_use]
57 pub fn has_v2(&self) -> bool {
58 self.v2.is_some()
59 }
60
61 #[must_use]
63 pub fn is_hybrid(&self) -> bool {
64 self.v1.is_some() && self.v2.is_some()
65 }
66
67 #[must_use]
72 pub fn best_v1(&self) -> Id20 {
73 if let Some(v1) = self.v1 {
74 v1
75 } else if let Some(v2) = self.v2 {
76 let mut truncated = [0u8; 20];
77 truncated.copy_from_slice(&v2.0[..20]);
78 Id20(truncated)
79 } else {
80 Id20::ZERO
81 }
82 }
83}
84
85impl std::fmt::Display for InfoHashes {
86 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87 match (&self.v1, &self.v2) {
88 (Some(v1), Some(v2)) => write!(f, "v1:{v1} v2:{v2}"),
89 (Some(v1), None) => write!(f, "v1:{v1}"),
90 (None, Some(v2)) => write!(f, "v2:{v2}"),
91 (None, None) => write!(f, "<no hash>"),
92 }
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn v1_only_construction() {
102 let hash = Id20::from_hex("aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d").unwrap();
103 let ih = InfoHashes::v1_only(hash);
104 assert!(ih.has_v1());
105 assert!(!ih.has_v2());
106 assert!(!ih.is_hybrid());
107 assert_eq!(ih.best_v1(), hash);
108 }
109
110 #[test]
111 fn v2_only_construction() {
112 let hash =
113 Id32::from_hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
114 .unwrap();
115 let ih = InfoHashes::v2_only(hash);
116 assert!(!ih.has_v1());
117 assert!(ih.has_v2());
118 assert!(!ih.is_hybrid());
119 assert_eq!(ih.v2, Some(hash));
120 }
121
122 #[test]
123 fn hybrid_construction() {
124 let v1 = Id20::from_hex("aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d").unwrap();
125 let v2 = Id32::from_hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
126 .unwrap();
127 let ih = InfoHashes::hybrid(v1, v2);
128 assert!(ih.has_v1());
129 assert!(ih.has_v2());
130 assert!(ih.is_hybrid());
131 assert_eq!(ih.best_v1(), v1);
132 }
133
134 #[test]
135 fn best_v1_truncation_from_v2() {
136 let v2 = Id32::from_hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
137 .unwrap();
138 let ih = InfoHashes::v2_only(v2);
139 let truncated = ih.best_v1();
140 assert_eq!(
142 truncated.to_hex(),
143 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4"
144 );
145 }
146}