bitcoin_units/locktime/absolute/
error.rs1use core::convert::Infallible;
6use core::fmt;
7
8use internals::error::InputString;
9#[cfg(feature = "encoding")]
10use internals::write_err;
11
12use super::{Height, MedianTimePast, LOCK_TIME_THRESHOLD};
13use crate::parse_int::ParseIntError;
14
15#[cfg(feature = "encoding")]
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct LockTimeDecoderError(pub(super) encoding::UnexpectedEofError);
19
20#[cfg(feature = "encoding")]
21impl From<Infallible> for LockTimeDecoderError {
22 fn from(never: Infallible) -> Self { match never {} }
23}
24
25#[cfg(feature = "encoding")]
26impl fmt::Display for LockTimeDecoderError {
27 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
28 write_err!(f, "lock time decoder error"; self.0)
29 }
30}
31
32#[cfg(all(feature = "std", feature = "encoding"))]
33impl std::error::Error for LockTimeDecoderError {
34 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.0) }
35}
36
37#[derive(Debug, Clone, PartialEq, Eq)]
39pub struct IncompatibleHeightError {
40 pub(super) lock: MedianTimePast,
42 pub(super) incompatible: Height,
44}
45
46impl IncompatibleHeightError {
47 pub fn lock(&self) -> MedianTimePast { self.lock }
49
50 pub fn incompatible(&self) -> Height { self.incompatible }
52}
53
54impl fmt::Display for IncompatibleHeightError {
55 #[inline]
56 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
57 write!(
58 f,
59 "tried to satisfy a lock-by-time lock {} with height: {}",
60 self.lock, self.incompatible
61 )
62 }
63}
64
65#[cfg(feature = "std")]
66impl std::error::Error for IncompatibleHeightError {}
67
68#[derive(Debug, Clone, PartialEq, Eq)]
70pub struct IncompatibleTimeError {
71 pub(super) lock: Height,
73 pub(super) incompatible: MedianTimePast,
75}
76
77impl IncompatibleTimeError {
78 pub fn lock(&self) -> Height { self.lock }
80
81 pub fn incompatible(&self) -> MedianTimePast { self.incompatible }
83}
84
85impl fmt::Display for IncompatibleTimeError {
86 #[inline]
87 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
88 write!(
89 f,
90 "tried to satisfy a lock-by-height lock {} with MTP: {}",
91 self.lock, self.incompatible
92 )
93 }
94}
95
96#[cfg(feature = "std")]
97impl std::error::Error for IncompatibleTimeError {}
98
99#[derive(Debug, Clone, Eq, PartialEq)]
101pub struct ParseHeightError(ParseError);
102
103impl fmt::Display for ParseHeightError {
104 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
105 self.0.display(f, "block height", 0, LOCK_TIME_THRESHOLD - 1)
106 }
107}
108
109#[cfg(feature = "std")]
110impl std::error::Error for ParseHeightError {
111 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { self.0.source() }
113}
114
115impl From<ParseError> for ParseHeightError {
116 fn from(value: ParseError) -> Self { Self(value) }
117}
118
119#[derive(Debug, Clone, Eq, PartialEq)]
121pub struct ParseTimeError(ParseError);
122
123impl fmt::Display for ParseTimeError {
124 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
125 self.0.display(f, "block time", LOCK_TIME_THRESHOLD, u32::MAX)
126 }
127}
128
129#[cfg(feature = "std")]
130impl std::error::Error for ParseTimeError {
131 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { self.0.source() }
133}
134
135impl From<ParseError> for ParseTimeError {
136 fn from(value: ParseError) -> Self { Self(value) }
137}
138
139#[derive(Debug, Clone, Eq, PartialEq)]
141pub(super) enum ParseError {
142 ParseInt(ParseIntError),
143 Conversion(i64),
146}
147
148impl From<Infallible> for ParseError {
149 fn from(never: Infallible) -> Self { match never {} }
150}
151
152impl ParseError {
153 pub(super) fn invalid_int<S: Into<InputString>>(
154 s: S,
155 ) -> impl FnOnce(core::num::ParseIntError) -> Self {
156 move |source| {
157 Self::ParseInt(ParseIntError { input: s.into(), bits: 32, is_signed: true, source })
158 }
159 }
160
161 pub(super) fn display(
162 &self,
163 f: &mut fmt::Formatter<'_>,
164 subject: &str,
165 lower_bound: u32,
166 upper_bound: u32,
167 ) -> fmt::Result {
168 use core::num::IntErrorKind;
169
170 match self {
171 Self::ParseInt(ParseIntError { input, bits: _, is_signed: _, source })
172 if *source.kind() == IntErrorKind::PosOverflow =>
173 {
174 write!(
176 f,
177 "{} ({} is above limit {})",
178 input.display_cannot_parse("absolute Height/MedianTimePast"),
179 subject,
180 upper_bound
181 )
182 }
183 Self::ParseInt(ParseIntError { input, bits: _, is_signed: _, source })
184 if *source.kind() == IntErrorKind::NegOverflow =>
185 {
186 write!(
188 f,
189 "{} ({} is below limit {})",
190 input.display_cannot_parse("absolute Height/MedianTimePast"),
191 subject,
192 lower_bound
193 )
194 }
195 Self::ParseInt(ParseIntError { input, bits: _, is_signed: _, source: _ }) => {
196 write!(
197 f,
198 "{} ({})",
199 input.display_cannot_parse("absolute Height/MedianTimePast"),
200 subject
201 )
202 }
203 Self::Conversion(value) if *value < i64::from(lower_bound) => {
204 write!(f, "{} {} is below limit {}", subject, value, lower_bound)
205 }
206 Self::Conversion(value) => {
207 write!(f, "{} {} is above limit {}", subject, value, upper_bound)
208 }
209 }
210 }
211
212 #[cfg(feature = "std")]
214 pub(super) fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
215 use core::num::IntErrorKind;
216
217 match self {
218 Self::ParseInt(ParseIntError { source, .. })
219 if *source.kind() == IntErrorKind::PosOverflow =>
220 None,
221 Self::ParseInt(ParseIntError { source, .. })
222 if *source.kind() == IntErrorKind::NegOverflow =>
223 None,
224 Self::ParseInt(ParseIntError { source, .. }) => Some(source),
225 Self::Conversion(_) => None,
226 }
227 }
228}
229
230impl From<ConversionError> for ParseError {
231 fn from(value: ConversionError) -> Self { Self::Conversion(value.input.into()) }
232}
233
234#[derive(Debug, Clone, PartialEq, Eq)]
236#[non_exhaustive]
237pub struct ConversionError {
238 unit: LockTimeUnit,
240 input: u32,
242}
243
244impl ConversionError {
245 pub(super) const fn invalid_height(n: u32) -> Self {
247 Self { unit: LockTimeUnit::Blocks, input: n }
248 }
249
250 pub(super) const fn invalid_time(n: u32) -> Self {
252 Self { unit: LockTimeUnit::Seconds, input: n }
253 }
254}
255
256impl fmt::Display for ConversionError {
257 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
258 write!(f, "invalid lock time value {}, {}", self.input, self.unit)
259 }
260}
261
262#[cfg(feature = "std")]
263impl std::error::Error for ConversionError {
264 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
265}
266
267#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
269enum LockTimeUnit {
270 Blocks,
272 Seconds,
274}
275
276impl fmt::Display for LockTimeUnit {
277 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
278 match *self {
279 Self::Blocks =>
280 write!(f, "expected lock-by-height (must be < {})", LOCK_TIME_THRESHOLD),
281 Self::Seconds =>
282 write!(f, "expected lock-by-time (must be >= {})", LOCK_TIME_THRESHOLD),
283 }
284 }
285}
286
287#[cfg(test)]
288mod tests {
289 #[test]
290 #[cfg(feature = "alloc")]
291 fn locktime_unit_display() {
292 use alloc::format;
293
294 use super::LockTimeUnit;
295
296 let blocks = LockTimeUnit::Blocks;
297 let seconds = LockTimeUnit::Seconds;
298
299 assert_eq!(format!("{}", blocks), "expected lock-by-height (must be < 500000000)");
300 assert_eq!(format!("{}", seconds), "expected lock-by-time (must be >= 500000000)");
301 }
302}