//! 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 as `. /// /// 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 '' 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 ` is not a known `. /// /// 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 "'' is not a known foo" /// self.0.unknown_variant("foo", f) /// } /// } /// ``` pub fn unknown_variant(&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 as `. /// /// 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) implementation 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 fmt::Display for CannotParse<'_, 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("") } } impl From<&str> for Storage { fn from(_value: &str) -> Self { Storage } } pub(super) fn cannot_parse(_: &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(_: &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(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(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, alloc::borrow::Cow<'_, str>); }