parity_scale_codec/
error.rs

1// Copyright 2021 Parity Technologies
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Error type, descriptive or undescriptive depending on features.
16
17use crate::alloc::borrow::Cow;
18#[cfg(feature = "chain-error")]
19use crate::alloc::boxed::Box;
20
21/// Error type.
22///
23/// Descriptive on `std` environment, with chaining error on `chain-error` environment,
24/// underscriptive otherwise.
25#[derive(PartialEq, Eq, Clone, Debug)]
26pub struct Error {
27	#[cfg(feature = "chain-error")]
28	cause: Option<Box<Error>>,
29	#[cfg(feature = "chain-error")]
30	desc: Cow<'static, str>,
31}
32
33impl Error {
34	/// Chain error message with description.
35	///
36	/// When compiled with `chain-error` feature, the description is chained, otherwise the
37	/// description is ditched.
38	pub fn chain(self, desc: impl Into<Cow<'static, str>>) -> Self {
39		#[cfg(feature = "chain-error")]
40		{
41			Self { desc: desc.into(), cause: Some(Box::new(self)) }
42		}
43
44		#[cfg(not(feature = "chain-error"))]
45		{
46			let _ = desc;
47			self
48		}
49	}
50
51	/// Display error with indentation.
52	#[cfg(feature = "chain-error")]
53	fn display_with_indent(&self, indent: u32, f: &mut core::fmt::Formatter) -> core::fmt::Result {
54		for _ in 0..indent {
55			f.write_str("\t")?;
56		}
57		f.write_str(&self.desc)?;
58		if let Some(cause) = &self.cause {
59			f.write_str(":")?;
60			f.write_str("\n")?;
61			cause.display_with_indent(indent + 1, f)
62		} else {
63			// Only return to new line if the error has been displayed with some indent,
64			// i.e. if the error has some causes.
65			if indent != 0 {
66				f.write_str("\n")?;
67			}
68			Ok(())
69		}
70	}
71}
72
73impl core::fmt::Display for Error {
74	fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
75		#[cfg(feature = "chain-error")]
76		{
77			self.display_with_indent(0, f)
78		}
79
80		#[cfg(not(feature = "chain-error"))]
81		{
82			f.write_str("Codec error")
83		}
84	}
85}
86
87impl From<&'static str> for Error {
88	fn from(desc: &'static str) -> Error {
89		#[cfg(feature = "chain-error")]
90		{
91			Error { desc: desc.into(), cause: None }
92		}
93
94		#[cfg(not(feature = "chain-error"))]
95		{
96			let _ = desc;
97			Error {}
98		}
99	}
100}
101
102#[cfg(feature = "std")]
103impl std::error::Error for Error {
104	fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
105		#[cfg(feature = "chain-error")]
106		{
107			self.cause.as_ref().map(|e| e as &(dyn std::error::Error + 'static))
108		}
109
110		#[cfg(not(feature = "chain-error"))]
111		{
112			None
113		}
114	}
115}
116
117#[cfg(test)]
118mod tests {
119	use crate::Error;
120
121	#[test]
122	fn test_full_error() {
123		let msg: &str = "final type:\n\twrap cause:\n\t\troot cause\n";
124
125		let error = Error::from("root cause").chain("wrap cause").chain("final type");
126
127		assert_eq!(&error.to_string(), msg);
128	}
129
130	#[test]
131	fn impl_std_error() {
132		use std::error::Error as _;
133
134		let error = Error::from("root cause").chain("wrap cause").chain("final type");
135		let s = error.source().unwrap();
136
137		assert_eq!(&s.to_string(), "wrap cause:\n\troot cause\n");
138	}
139}