1#[cfg(feature = "encoding")]
6use core::convert::Infallible;
7use core::fmt;
8
9#[cfg(feature = "arbitrary")]
10use arbitrary::{Arbitrary, Unstructured};
11#[cfg(feature = "encoding")]
12use internals::write_err;
13#[cfg(feature = "serde")]
14use serde::{Deserialize, Serialize};
15
16use crate::parse_int::{self, PrefixedHexError, UnprefixedHexError};
17
18#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
33#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
34pub struct CompactTarget(u32);
35
36impl CompactTarget {
37 #[inline]
39 pub fn from_consensus(bits: u32) -> Self { Self(bits) }
40
41 #[inline]
43 pub const fn to_consensus(self) -> u32 { self.0 }
44
45 #[cfg(feature = "alloc")]
47 #[inline]
48 #[deprecated(since = "1.0.0-rc.0", note = "use `format!(\"{var:x}\")` instead")]
49 pub fn to_hex(self) -> alloc::string::String { alloc::format!("{:x}", self) }
50
51 pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError>
58 where
59 Self: Sized
60 {
61 let target = parse_int::hex_u32_prefixed(s)?;
62 Ok(Self::from_consensus(target))
63 }
64
65 pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError>
72 where
73 Self: Sized
74 {
75 let target = parse_int::hex_u32_unprefixed(s)?;
76 Ok(Self::from_consensus(target))
77 }
78}
79
80impl fmt::Display for CompactTarget {
81 #[inline]
82 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
83}
84
85impl fmt::LowerHex for CompactTarget {
86 #[inline]
87 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(&self.0, f) }
88}
89
90impl fmt::UpperHex for CompactTarget {
91 #[inline]
92 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::UpperHex::fmt(&self.0, f) }
93}
94
95impl fmt::Octal for CompactTarget {
96 #[inline]
97 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Octal::fmt(&self.0, f) }
98}
99
100impl fmt::Binary for CompactTarget {
101 #[inline]
102 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Binary::fmt(&self.0, f) }
103}
104
105#[cfg(feature = "encoding")]
106encoding::encoder_newtype_exact! {
107 pub struct CompactTargetEncoder<'e>(encoding::ArrayEncoder<4>);
109}
110
111#[cfg(feature = "encoding")]
112impl encoding::Encodable for CompactTarget {
113 type Encoder<'e> = CompactTargetEncoder<'e>;
114 fn encoder(&self) -> Self::Encoder<'_> {
115 CompactTargetEncoder::new(encoding::ArrayEncoder::without_length_prefix(
116 self.to_consensus().to_le_bytes(),
117 ))
118 }
119}
120
121#[cfg(feature = "encoding")]
123pub struct CompactTargetDecoder(encoding::ArrayDecoder<4>);
124
125#[cfg(feature = "encoding")]
126impl CompactTargetDecoder {
127 pub const fn new() -> Self { Self(encoding::ArrayDecoder::new()) }
129}
130
131#[cfg(feature = "encoding")]
132impl Default for CompactTargetDecoder {
133 fn default() -> Self { Self::new() }
134}
135
136#[cfg(feature = "encoding")]
137impl encoding::Decoder for CompactTargetDecoder {
138 type Output = CompactTarget;
139 type Error = CompactTargetDecoderError;
140
141 #[inline]
142 fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
143 self.0.push_bytes(bytes).map_err(CompactTargetDecoderError)
144 }
145
146 #[inline]
147 fn end(self) -> Result<Self::Output, Self::Error> {
148 let n = u32::from_le_bytes(self.0.end().map_err(CompactTargetDecoderError)?);
149 Ok(CompactTarget::from_consensus(n))
150 }
151
152 #[inline]
153 fn read_limit(&self) -> usize { self.0.read_limit() }
154}
155
156#[cfg(feature = "encoding")]
157impl encoding::Decodable for CompactTarget {
158 type Decoder = CompactTargetDecoder;
159 fn decoder() -> Self::Decoder { CompactTargetDecoder(encoding::ArrayDecoder::<4>::new()) }
160}
161
162#[derive(Debug, Clone, PartialEq, Eq)]
164#[cfg(feature = "encoding")]
165pub struct CompactTargetDecoderError(encoding::UnexpectedEofError);
166
167#[cfg(feature = "encoding")]
168impl From<Infallible> for CompactTargetDecoderError {
169 fn from(never: Infallible) -> Self { match never {} }
170}
171
172#[cfg(feature = "encoding")]
173impl fmt::Display for CompactTargetDecoderError {
174 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
175 write_err!(f, "sequence decoder error"; self.0)
176 }
177}
178
179#[cfg(feature = "std")]
180#[cfg(feature = "encoding")]
181impl std::error::Error for CompactTargetDecoderError {
182 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.0) }
183}
184
185#[cfg(feature = "arbitrary")]
186impl<'a> Arbitrary<'a> for CompactTarget {
187 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
188 Ok(Self::from_consensus(u.arbitrary()?))
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 #[cfg(feature = "alloc")]
195 use alloc::format;
196 #[cfg(feature = "alloc")]
197 use alloc::string::ToString;
198 #[cfg(feature = "std")]
199 use std::error::Error as _;
200
201 #[cfg(feature = "encoding")]
202 use encoding::Decoder as _;
203
204 use super::*;
205
206 #[test]
207 #[cfg(feature = "encoding")]
208 fn compact_target_decoder_read_limit() {
209 assert_eq!(CompactTargetDecoder::default().read_limit(), 4);
211 assert_eq!(<CompactTarget as encoding::Decodable>::decoder().read_limit(), 4);
212 }
213
214 #[test]
215 #[cfg(feature = "encoding")]
216 fn compact_target_decoder_round_trip() {
217 let bits: u32 = 0x1d00_ffff;
218 let compact_target =
219 encoding::decode_from_slice::<CompactTarget>(&bits.to_le_bytes()).unwrap();
220 assert_eq!(compact_target.to_consensus(), bits);
221 }
222
223 #[test]
224 #[cfg(feature = "alloc")]
225 #[allow(deprecated)]
226 fn compact_target_to_hex() {
227 let compact_target = CompactTarget::from_consensus(0x1d00_ffff);
228 assert_eq!(compact_target.to_hex(), "1d00ffff");
229 }
230
231 #[test]
232 #[cfg(feature = "encoding")]
233 #[cfg(feature = "alloc")]
234 fn compact_target_decoder_error_display_and_source() {
235 let mut slice = [0u8; 3].as_slice();
236 let mut decoder = CompactTargetDecoder::new();
237
238 assert!(decoder.push_bytes(&mut slice).unwrap());
239
240 let err = decoder.end().unwrap_err();
241 assert!(!err.to_string().is_empty());
242 #[cfg(feature = "std")]
243 assert!(err.source().is_some());
244 }
245
246 #[test]
247 fn compact_target_ordering() {
248 let lower = CompactTarget::from_consensus(0x1d00_fffe);
249 let lower_copy = CompactTarget::from_consensus(0x1d00_fffe);
250 let higher = CompactTarget::from_consensus(0x1d00_ffff);
251
252 assert!(lower < higher);
253 assert!(lower == lower_copy);
254 }
255
256 #[test]
257 #[cfg(feature = "alloc")]
258 fn compact_target_formatting() {
259 let compact_target = CompactTarget::from_consensus(0x1d00_ffff);
260 assert_eq!(format!("{}", compact_target), "486604799");
261 assert_eq!(format!("{:x}", compact_target), "1d00ffff");
262 assert_eq!(format!("{:#x}", compact_target), "0x1d00ffff");
263 assert_eq!(format!("{:X}", compact_target), "1D00FFFF");
264 assert_eq!(format!("{:#X}", compact_target), "0x1D00FFFF");
265 assert_eq!(format!("{:o}", compact_target), "3500177777");
266 assert_eq!(format!("{:#o}", compact_target), "0o3500177777");
267 assert_eq!(format!("{:b}", compact_target), "11101000000001111111111111111");
268 assert_eq!(format!("{:#b}", compact_target), "0b11101000000001111111111111111");
269 assert_eq!(compact_target.to_consensus(), 0x1d00_ffff);
270 }
271
272 #[test]
273 fn compact_target_from_hex_lower() {
274 let target = CompactTarget::from_hex("0x010034ab").unwrap();
275 assert_eq!(target, CompactTarget::from_consensus(0x0100_34ab));
276 }
277
278 #[test]
279 fn compact_target_from_hex_upper() {
280 let target = CompactTarget::from_hex("0X010034AB").unwrap();
281 assert_eq!(target, CompactTarget::from_consensus(0x0100_34ab));
282 }
283
284 #[test]
285 fn compact_target_from_unprefixed_hex_lower() {
286 let target = CompactTarget::from_unprefixed_hex("010034ab").unwrap();
287 assert_eq!(target, CompactTarget::from_consensus(0x0100_34ab));
288 }
289
290 #[test]
291 fn compact_target_from_unprefixed_hex_upper() {
292 let target = CompactTarget::from_unprefixed_hex("010034AB").unwrap();
293 assert_eq!(target, CompactTarget::from_consensus(0x0100_34ab));
294 }
295
296 #[test]
297 fn compact_target_from_hex_invalid_hex_should_err() {
298 let hex = "0xzbf9";
299 let result = CompactTarget::from_hex(hex);
300 assert!(result.is_err());
301 }
302
303 #[test]
304 #[cfg(feature = "alloc")]
305 fn compact_target_lower_hex_and_upper_hex() {
306 assert_eq!(format!("{:08x}", CompactTarget::from_consensus(0x01D0_F456)), "01d0f456");
307 assert_eq!(format!("{:08X}", CompactTarget::from_consensus(0x01d0_f456)), "01D0F456");
308 }
309}