|
5 | 5 |
|
6 | 6 | mod error;
|
7 | 7 |
|
| 8 | +use core::ops; |
8 | 9 | use core::str::FromStr;
|
9 |
| -use core::{fmt, ops}; |
10 | 10 |
|
11 |
| -pub use self::error::{ParseThresholdError, ParseTreeError}; |
| 11 | +pub use self::error::{ParseNumError, ParseThresholdError, ParseTreeError}; |
12 | 12 | use crate::blanket_traits::StaticDebugAndDisplay;
|
13 | 13 | use crate::descriptor::checksum::verify_checksum;
|
14 | 14 | use crate::iter::{self, TreeLike};
|
15 | 15 | use crate::prelude::*;
|
16 |
| -use crate::{errstr, Error, ParseError, Threshold, MAX_RECURSION_DEPTH}; |
| 16 | +use crate::{AbsLockTime, Error, ParseError, RelLockTime, Threshold, MAX_RECURSION_DEPTH}; |
17 | 17 |
|
18 | 18 | /// Allowed characters are descriptor strings.
|
19 | 19 | pub const INPUT_CHARSET: &str = "0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#\"\\ ";
|
@@ -138,6 +138,38 @@ impl<'a> Tree<'a> {
|
138 | 138 | }
|
139 | 139 | }
|
140 | 140 |
|
| 141 | + /// Check that a tree node has a single terminal child which is an absolute locktime. |
| 142 | + /// |
| 143 | + /// Returns an error assuming that the node is named "after". |
| 144 | + /// |
| 145 | + /// If so, parse the locktime from a string and return it. |
| 146 | + pub fn verify_after(&self) -> Result<AbsLockTime, ParseError> { |
| 147 | + self.verify_n_children("after", 1..=1) |
| 148 | + .map_err(ParseError::Tree)?; |
| 149 | + self.args[0] |
| 150 | + .verify_n_children("absolute locktime", 0..=0) |
| 151 | + .map_err(ParseError::Tree)?; |
| 152 | + parse_num(self.args[0].name) |
| 153 | + .map_err(ParseError::Num) |
| 154 | + .and_then(|n| AbsLockTime::from_consensus(n).map_err(ParseError::AbsoluteLockTime)) |
| 155 | + } |
| 156 | + |
| 157 | + /// Check that a tree node has a single terminal child which is a relative locktime. |
| 158 | + /// |
| 159 | + /// Returns an error assuming that the node is named "older". |
| 160 | + /// |
| 161 | + /// If so, parse the locktime from a string and return it. |
| 162 | + pub fn verify_older(&self) -> Result<RelLockTime, ParseError> { |
| 163 | + self.verify_n_children("older", 1..=1) |
| 164 | + .map_err(ParseError::Tree)?; |
| 165 | + self.args[0] |
| 166 | + .verify_n_children("relative locktime", 0..=0) |
| 167 | + .map_err(ParseError::Tree)?; |
| 168 | + parse_num(self.args[0].name) |
| 169 | + .map_err(ParseError::Num) |
| 170 | + .and_then(|n| RelLockTime::from_consensus(n).map_err(ParseError::RelativeLockTime)) |
| 171 | + } |
| 172 | + |
141 | 173 | /// Check that a tree node is a terminal (has no children).
|
142 | 174 | ///
|
143 | 175 | /// If so, parse the terminal from a string and return it.
|
@@ -377,34 +409,23 @@ impl<'a> Tree<'a> {
|
377 | 409 | return Err(ParseThresholdError::KNotTerminal);
|
378 | 410 | }
|
379 | 411 |
|
380 |
| - let k = parse_num(self.args[0].name) |
381 |
| - .map_err(|e| ParseThresholdError::ParseK(e.to_string()))? as usize; |
| 412 | + let k = parse_num(self.args[0].name).map_err(ParseThresholdError::ParseK)? as usize; |
382 | 413 | Threshold::new(k, vec![(); self.args.len() - 1]).map_err(ParseThresholdError::Threshold)
|
383 | 414 | }
|
384 | 415 | }
|
385 | 416 |
|
386 | 417 | /// Parse a string as a u32, for timelocks or thresholds
|
387 |
| -pub fn parse_num(s: &str) -> Result<u32, Error> { |
388 |
| - if s.len() > 1 { |
389 |
| - let ch = s.chars().next().unwrap(); |
| 418 | +pub fn parse_num(s: &str) -> Result<u32, ParseNumError> { |
| 419 | + if s == "0" { |
| 420 | + // Special-case 0 since it is the only number which may start with a leading zero. |
| 421 | + return Ok(0); |
| 422 | + } |
| 423 | + if let Some(ch) = s.chars().next() { |
390 | 424 | if !('1'..='9').contains(&ch) {
|
391 |
| - return Err(Error::Unexpected("Number must start with a digit 1-9".to_string())); |
| 425 | + return Err(ParseNumError::InvalidLeadingDigit(ch)); |
392 | 426 | }
|
393 | 427 | }
|
394 |
| - u32::from_str(s).map_err(|_| errstr(s)) |
395 |
| -} |
396 |
| - |
397 |
| -/// Attempts to parse a terminal expression |
398 |
| -pub fn terminal<T, F, Err>(term: &Tree, convert: F) -> Result<T, Error> |
399 |
| -where |
400 |
| - F: FnOnce(&str) -> Result<T, Err>, |
401 |
| - Err: fmt::Display, |
402 |
| -{ |
403 |
| - if term.args.is_empty() { |
404 |
| - convert(term.name).map_err(|e| Error::Unexpected(e.to_string())) |
405 |
| - } else { |
406 |
| - Err(errstr(term.name)) |
407 |
| - } |
| 428 | + u32::from_str(s).map_err(ParseNumError::StdParse) |
408 | 429 | }
|
409 | 430 |
|
410 | 431 | #[cfg(test)]
|
|
0 commit comments