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    pub fn unknown_variant<T>(&self, what: &T, f: &mut fmt::Formatter) -> fmt::Result
72    where
73        T: fmt::Display + ?Sized,
74    {
75        storage::unknown_variant(&self.0, what, f)
76    }
77}
78
79macro_rules! impl_from {
80    ($($type:ty),+ $(,)?) => {
81        $(
82            impl From<$type> for InputString {
83                fn from(input: $type) -> Self {
84                    #[allow(clippy::useless_conversion)]
85                    InputString(input.into())
86                }
87            }
88        )+
89    }
90}
91
92impl_from!(&str);
93
94/// Displays message saying `failed to parse <input> as <what>`.
95///
96/// This is created by `display_cannot_parse` method and should be used as
97/// `write_err!("{}", self.input.display_cannot_parse("what is parsed"); self.source)` in parse
98/// error [`Display`](fmt::Display) imlementation if the error has source. If the error doesn't
99/// have a source just use regular `write!` with same formatting arguments.
100pub struct CannotParse<'a, T: fmt::Display + ?Sized> {
101    input: &'a InputString,
102    what: &'a T,
103}
104
105impl<'a, T: fmt::Display + ?Sized> fmt::Display for CannotParse<'a, T> {
106    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
107        storage::cannot_parse(&self.input.0, &self.what, f)
108    }
109}
110
111#[cfg(not(feature = "alloc"))]
112mod storage {
113    use core::fmt;
114
115    #[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
116    pub(super) struct Storage;
117
118    impl fmt::Debug for Storage {
119        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
120            f.write_str("<unknown input string - compiled without the `alloc` feature>")
121        }
122    }
123
124    impl From<&str> for Storage {
125        fn from(_value: &str) -> Self { Storage }
126    }
127
128    pub(super) fn cannot_parse<W>(_: &Storage, what: &W, f: &mut fmt::Formatter) -> fmt::Result
129    where
130        W: fmt::Display + ?Sized,
131    {
132        write!(f, "failed to parse {}", what)
133    }
134
135    pub(super) fn unknown_variant<W>(_: &Storage, what: &W, f: &mut fmt::Formatter) -> fmt::Result
136    where
137        W: fmt::Display + ?Sized,
138    {
139        write!(f, "unknown {}", what)
140    }
141}
142
143#[cfg(feature = "alloc")]
144mod storage {
145    use core::fmt;
146
147    use super::InputString;
148
149    pub(super) type Storage = alloc::string::String;
150
151    pub(super) fn cannot_parse<W>(input: &Storage, what: &W, f: &mut fmt::Formatter) -> fmt::Result
152    where
153        W: fmt::Display + ?Sized,
154    {
155        write!(f, "failed to parse '{}' as {}", input, what)
156    }
157
158    pub(super) fn unknown_variant<W>(inp: &Storage, what: &W, f: &mut fmt::Formatter) -> fmt::Result
159    where
160        W: fmt::Display + ?Sized,
161    {
162        write!(f, "'{}' is not a known {}", inp, what)
163    }
164
165    impl_from!(alloc::string::String, alloc::boxed::Box<str>, alloc::borrow::Cow<'_, str>);
166}