1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
//! Implements the [`InputString`] type storing the parsed input.
use core::fmt;
use storage::Storage;
/// Conditionally stores the input string in parse errors.
///
/// This type stores the input string of a parse function depending on whether `alloc` feature is
/// enabled. When it is enabled, the string is stored inside as `String`. When disabled this is a
/// zero-sized type and attempt to store a string does nothing.
///
/// This provides two methods to format the error strings depending on the context.
#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct InputString(Storage);
impl InputString {
/// Displays a message saying `failed to parse <self> as <what>`.
///
/// This is normally used with the `write_err!` macro.
///
/// # Examples
///
/// ```
/// use core::fmt;
/// use bitcoin_internals::error::InputString;
/// use bitcoin_internals::write_err;
///
/// /// An example parsing error including the parse error from core.
/// #[derive(Debug, Clone, PartialEq, Eq)]
/// pub struct ParseError {
/// input: InputString,
/// error: core::num::ParseIntError,
/// }
///
/// impl fmt::Display for ParseError {
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
/// // Outputs "failed to parse '<input string>' as foo"
/// write_err!(f, "{}", self.input.display_cannot_parse("foo"); self.error)
/// }
/// }
/// ```
pub fn display_cannot_parse<'a, T>(&'a self, what: &'a T) -> CannotParse<'a, T>
where
T: fmt::Display + ?Sized,
{
CannotParse { input: self, what }
}
/// Formats a message saying `<self> is not a known <what>`.
///
/// This is normally used in leaf parse errors (with no source) when parsing an enum.
///
/// # Examples
///
/// ```
/// use core::fmt;
/// use bitcoin_internals::error::InputString;
///
/// /// An example parsing error.
/// #[derive(Debug, Clone, PartialEq, Eq)]
/// pub struct ParseError(InputString);
///
/// impl fmt::Display for ParseError {
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
/// // Outputs "'<input string>' is not a known foo"
/// self.0.unknown_variant("foo", f)
/// }
/// }
/// ```
pub fn unknown_variant<T>(&self, what: &T, f: &mut fmt::Formatter) -> fmt::Result
where
T: fmt::Display + ?Sized,
{
storage::unknown_variant(&self.0, what, f)
}
}
macro_rules! impl_from {
($($type:ty),+ $(,)?) => {
$(
impl From<$type> for InputString {
fn from(input: $type) -> Self {
#[allow(clippy::useless_conversion)]
InputString(input.into())
}
}
)+
}
}
impl_from!(&str);
/// Displays message saying `failed to parse <input> as <what>`.
///
/// This is created by `display_cannot_parse` method and should be used as
/// `write_err!("{}", self.input.display_cannot_parse("what is parsed"); self.source)` in parse
/// error [`Display`](fmt::Display) imlementation if the error has source. If the error doesn't
/// have a source just use regular `write!` with same formatting arguments.
pub struct CannotParse<'a, T: fmt::Display + ?Sized> {
input: &'a InputString,
what: &'a T,
}
impl<'a, T: fmt::Display + ?Sized> fmt::Display for CannotParse<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
storage::cannot_parse(&self.input.0, &self.what, f)
}
}
#[cfg(not(feature = "alloc"))]
mod storage {
use core::fmt;
#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub(super) struct Storage;
impl fmt::Debug for Storage {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("<unknown input string - compiled without the `alloc` feature>")
}
}
impl From<&str> for Storage {
fn from(_value: &str) -> Self { Storage }
}
pub(super) fn cannot_parse<W>(_: &Storage, what: &W, f: &mut fmt::Formatter) -> fmt::Result
where
W: fmt::Display + ?Sized,
{
write!(f, "failed to parse {}", what)
}
pub(super) fn unknown_variant<W>(_: &Storage, what: &W, f: &mut fmt::Formatter) -> fmt::Result
where
W: fmt::Display + ?Sized,
{
write!(f, "unknown {}", what)
}
}
#[cfg(feature = "alloc")]
mod storage {
use core::fmt;
use super::InputString;
pub(super) type Storage = alloc::string::String;
pub(super) fn cannot_parse<W>(input: &Storage, what: &W, f: &mut fmt::Formatter) -> fmt::Result
where
W: fmt::Display + ?Sized,
{
write!(f, "failed to parse '{}' as {}", input, what)
}
pub(super) fn unknown_variant<W>(inp: &Storage, what: &W, f: &mut fmt::Formatter) -> fmt::Result
where
W: fmt::Display + ?Sized,
{
write!(f, "'{}' is not a known {}", inp, what)
}
impl_from!(alloc::string::String, alloc::boxed::Box<str>, alloc::borrow::Cow<'_, str>);
}