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}