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 whith the `write_err!` macro.
21    pub fn display_cannot_parse<'a, T>(&'a self, what: &'a T) -> CannotParse<'a, T>
22    where
23        T: fmt::Display + ?Sized,
24    {
25        CannotParse { input: self, what }
26    }
27
28    /// Formats a message saying `<self> is not a known <what>`.
29    ///
30    /// This is normally used in leaf parse errors (with no source) when parsing an enum.
31    pub fn unknown_variant<T>(&self, what: &T, f: &mut fmt::Formatter) -> fmt::Result
32    where
33        T: fmt::Display + ?Sized,
34    {
35        storage::unknown_variant(&self.0, what, f)
36    }
37}
38
39macro_rules! impl_from {
40    ($($type:ty),+ $(,)?) => {
41        $(
42            impl From<$type> for InputString {
43                fn from(input: $type) -> Self {
44                    #[allow(clippy::useless_conversion)]
45                    InputString(input.into())
46                }
47            }
48        )+
49    }
50}
51
52impl_from!(&str);
53
54/// Displays message saying `failed to parse <input> as <what>`.
55///
56/// This is created by `display_cannot_parse` method and should be used as
57/// `write_err!("{}", self.input.display_cannot_parse("what is parsed"); self.source)` in parse
58/// error [`Display`](fmt::Display) imlementation if the error has source. If the error doesn't
59/// have a source just use regular `write!` with same formatting arguments.
60pub struct CannotParse<'a, T: fmt::Display + ?Sized> {
61    input: &'a InputString,
62    what: &'a T,
63}
64
65impl<'a, T: fmt::Display + ?Sized> fmt::Display for CannotParse<'a, T> {
66    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
67        storage::cannot_parse(&self.input.0, &self.what, f)
68    }
69}
70
71#[cfg(not(feature = "alloc"))]
72mod storage {
73    use core::fmt;
74
75    #[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
76    pub(super) struct Storage;
77
78    impl fmt::Debug for Storage {
79        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
80            f.write_str("<unknown input string - compiled without the `alloc` feature>")
81        }
82    }
83
84    impl From<&str> for Storage {
85        fn from(_value: &str) -> Self { Storage }
86    }
87
88    pub(super) fn cannot_parse<W>(_: &Storage, what: &W, f: &mut fmt::Formatter) -> fmt::Result
89    where
90        W: fmt::Display + ?Sized,
91    {
92        write!(f, "failed to parse {}", what)
93    }
94
95    pub(super) fn unknown_variant<W>(_: &Storage, what: &W, f: &mut fmt::Formatter) -> fmt::Result
96    where
97        W: fmt::Display + ?Sized,
98    {
99        write!(f, "unknown {}", what)
100    }
101}
102
103#[cfg(feature = "alloc")]
104mod storage {
105    use core::fmt;
106
107    use super::InputString;
108
109    pub(super) type Storage = alloc::string::String;
110
111    pub(super) fn cannot_parse<W>(input: &Storage, what: &W, f: &mut fmt::Formatter) -> fmt::Result
112    where
113        W: fmt::Display + ?Sized,
114    {
115        write!(f, "failed to parse '{}' as {}", input, what)
116    }
117
118    pub(super) fn unknown_variant<W>(inp: &Storage, what: &W, f: &mut fmt::Formatter) -> fmt::Result
119    where
120        W: fmt::Display + ?Sized,
121    {
122        write!(f, "'{}' is not a known {}", inp, what)
123    }
124
125    impl_from!(alloc::string::String, alloc::boxed::Box<str>, alloc::borrow::Cow<'_, str>);
126}