1use core::fmt;
6
7use internals::write_err;
8
9use crate::parse::{self, ParseIntError};
10#[cfg(feature = "alloc")]
11use crate::prelude::*;
12
13pub const LOCK_TIME_THRESHOLD: u32 = 500_000_000;
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
28pub struct Height(u32);
29
30impl Height {
31 pub const ZERO: Self = Height(0);
33
34 pub const MIN: Self = Self::ZERO;
36
37 pub const MAX: Self = Height(LOCK_TIME_THRESHOLD - 1);
39
40 pub fn from_hex(s: &str) -> Result<Self, ParseHeightError> {
44 parse_hex(s, Self::from_consensus)
45 }
46
47 #[inline]
62 pub fn from_consensus(n: u32) -> Result<Height, ConversionError> {
63 if is_block_height(n) {
64 Ok(Self(n))
65 } else {
66 Err(ConversionError::invalid_height(n))
67 }
68 }
69
70 #[inline]
72 pub fn to_consensus_u32(self) -> u32 {
73 self.0
74 }
75}
76
77impl fmt::Display for Height {
78 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
79 fmt::Display::fmt(&self.0, f)
80 }
81}
82
83crate::impl_parse_str!(Height, ParseHeightError, parser(Height::from_consensus));
84
85#[derive(Debug, Clone, Eq, PartialEq)]
87pub struct ParseHeightError(ParseError);
88
89impl fmt::Display for ParseHeightError {
90 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
91 self.0
92 .display(f, "block height", 0, LOCK_TIME_THRESHOLD - 1)
93 }
94}
95
96#[cfg(feature = "std")]
97impl std::error::Error for ParseHeightError {
98 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
100 self.0.source()
101 }
102}
103
104impl From<ParseError> for ParseHeightError {
105 fn from(value: ParseError) -> Self {
106 Self(value)
107 }
108}
109
110#[cfg(feature = "serde")]
111impl<'de> serde::Deserialize<'de> for Height {
112 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
113 where
114 D: serde::Deserializer<'de>,
115 {
116 let u = u32::deserialize(deserializer)?;
117 Ok(Height::from_consensus(u).map_err(serde::de::Error::custom)?)
118 }
119}
120
121#[cfg(feature = "serde")]
122impl serde::Serialize for Height {
123 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
124 where
125 S: serde::Serializer,
126 {
127 self.to_consensus_u32().serialize(serializer)
128 }
129}
130
131#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
137pub struct Time(u32);
138
139impl Time {
140 pub const MIN: Self = Time(LOCK_TIME_THRESHOLD);
142
143 pub const MAX: Self = Time(u32::max_value());
145
146 pub fn from_hex(s: &str) -> Result<Self, ParseTimeError> {
150 parse_hex(s, Self::from_consensus)
151 }
152
153 #[inline]
168 pub fn from_consensus(n: u32) -> Result<Time, ConversionError> {
169 if is_block_time(n) {
170 Ok(Self(n))
171 } else {
172 Err(ConversionError::invalid_time(n))
173 }
174 }
175
176 #[inline]
178 pub fn to_consensus_u32(self) -> u32 {
179 self.0
180 }
181}
182
183impl fmt::Display for Time {
184 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
185 fmt::Display::fmt(&self.0, f)
186 }
187}
188
189crate::impl_parse_str!(Time, ParseTimeError, parser(Time::from_consensus));
190
191#[cfg(feature = "serde")]
192impl<'de> serde::Deserialize<'de> for Time {
193 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
194 where
195 D: serde::Deserializer<'de>,
196 {
197 let u = u32::deserialize(deserializer)?;
198 Ok(Time::from_consensus(u).map_err(serde::de::Error::custom)?)
199 }
200}
201
202#[cfg(feature = "serde")]
203impl serde::Serialize for Time {
204 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
205 where
206 S: serde::Serializer,
207 {
208 self.to_consensus_u32().serialize(serializer)
209 }
210}
211
212#[derive(Debug, Clone, Eq, PartialEq)]
214pub struct ParseTimeError(ParseError);
215
216impl fmt::Display for ParseTimeError {
217 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
218 self.0
219 .display(f, "block height", LOCK_TIME_THRESHOLD, u32::MAX)
220 }
221}
222
223#[cfg(feature = "std")]
224impl std::error::Error for ParseTimeError {
225 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
227 self.0.source()
228 }
229}
230
231impl From<ParseError> for ParseTimeError {
232 fn from(value: ParseError) -> Self {
233 Self(value)
234 }
235}
236
237fn parser<T, E, S, F>(f: F) -> impl FnOnce(S) -> Result<T, E>
238where
239 E: From<ParseError>,
240 S: AsRef<str> + Into<String>,
241 F: FnOnce(u32) -> Result<T, ConversionError>,
242{
243 move |s| {
244 let n = s
245 .as_ref()
246 .parse::<i64>()
247 .map_err(ParseError::invalid_int(s))?;
248 let n = u32::try_from(n).map_err(|_| ParseError::Conversion(n))?;
249 f(n).map_err(ParseError::from).map_err(Into::into)
250 }
251}
252
253fn parse_hex<T, E, S, F>(s: S, f: F) -> Result<T, E>
254where
255 E: From<ParseError>,
256 S: AsRef<str> + Into<String>,
257 F: FnOnce(u32) -> Result<T, ConversionError>,
258{
259 let n = i64::from_str_radix(parse::strip_hex_prefix(s.as_ref()), 16)
260 .map_err(ParseError::invalid_int(s))?;
261 let n = u32::try_from(n).map_err(|_| ParseError::Conversion(n))?;
262 f(n).map_err(ParseError::from).map_err(Into::into)
263}
264
265pub fn is_block_height(n: u32) -> bool {
267 n < LOCK_TIME_THRESHOLD
268}
269
270pub fn is_block_time(n: u32) -> bool {
272 n >= LOCK_TIME_THRESHOLD
273}
274
275#[derive(Debug, Clone, PartialEq, Eq)]
277#[non_exhaustive]
278pub struct ConversionError {
279 unit: LockTimeUnit,
281 input: u32,
283}
284
285impl ConversionError {
286 fn invalid_height(n: u32) -> Self {
288 Self {
289 unit: LockTimeUnit::Blocks,
290 input: n,
291 }
292 }
293
294 fn invalid_time(n: u32) -> Self {
296 Self {
297 unit: LockTimeUnit::Seconds,
298 input: n,
299 }
300 }
301}
302
303impl fmt::Display for ConversionError {
304 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
305 write!(f, "invalid lock time value {}, {}", self.input, self.unit)
306 }
307}
308
309#[cfg(feature = "std")]
310impl std::error::Error for ConversionError {
311 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
312 None
313 }
314}
315
316#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
318enum LockTimeUnit {
319 Blocks,
321 Seconds,
323}
324
325impl fmt::Display for LockTimeUnit {
326 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
327 use LockTimeUnit::*;
328
329 match *self {
330 Blocks => write!(
331 f,
332 "expected lock-by-blockheight (must be < {})",
333 LOCK_TIME_THRESHOLD
334 ),
335 Seconds => write!(
336 f,
337 "expected lock-by-blocktime (must be >= {})",
338 LOCK_TIME_THRESHOLD
339 ),
340 }
341 }
342}
343
344#[derive(Debug, Clone, Eq, PartialEq)]
346enum ParseError {
347 InvalidInteger {
348 source: core::num::ParseIntError,
349 input: String,
350 },
351 Conversion(i64),
354}
355
356internals::impl_from_infallible!(ParseError);
357
358impl ParseError {
359 fn invalid_int<S: Into<String>>(s: S) -> impl FnOnce(core::num::ParseIntError) -> Self {
360 move |source| Self::InvalidInteger {
361 source,
362 input: s.into(),
363 }
364 }
365
366 fn display(
367 &self,
368 f: &mut fmt::Formatter<'_>,
369 subject: &str,
370 lower_bound: u32,
371 upper_bound: u32,
372 ) -> fmt::Result {
373 use core::num::IntErrorKind;
374
375 use ParseError::*;
376
377 match self {
378 InvalidInteger { source, input } if *source.kind() == IntErrorKind::PosOverflow => {
379 write!(f, "{} {} is above limit {}", subject, input, upper_bound)
380 }
381 InvalidInteger { source, input } if *source.kind() == IntErrorKind::NegOverflow => {
382 write!(f, "{} {} is below limit {}", subject, input, lower_bound)
383 }
384 InvalidInteger { source, input } => {
385 write_err!(f, "failed to parse {} as {}", input, subject; source)
386 }
387 Conversion(value) if *value < i64::from(lower_bound) => {
388 write!(f, "{} {} is below limit {}", subject, value, lower_bound)
389 }
390 Conversion(value) => {
391 write!(f, "{} {} is above limit {}", subject, value, upper_bound)
392 }
393 }
394 }
395
396 #[cfg(feature = "std")]
398 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
399 use core::num::IntErrorKind;
400
401 use ParseError::*;
402
403 match self {
404 InvalidInteger { source, .. } if *source.kind() == IntErrorKind::PosOverflow => None,
405 InvalidInteger { source, .. } if *source.kind() == IntErrorKind::NegOverflow => None,
406 InvalidInteger { source, .. } => Some(source),
407 Conversion(_) => None,
408 }
409 }
410}
411
412impl From<ParseIntError> for ParseError {
413 fn from(value: ParseIntError) -> Self {
414 Self::InvalidInteger {
415 source: value.source,
416 input: value.input,
417 }
418 }
419}
420
421impl From<ConversionError> for ParseError {
422 fn from(value: ConversionError) -> Self {
423 Self::Conversion(value.input.into())
424 }
425}