bitcoin_internals/error/
input_string.rs

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