nu_protocol/errors/shell_error/
generic.rs1use crate::{
2 ShellError, Span,
3 shell_error::{ErrorSite, ErrorSource},
4};
5use miette::Diagnostic;
6use nu_utils::location::Location;
7use std::{
8 borrow::Cow,
9 error::Error as StdError,
10 fmt::{self, Display},
11 sync::Arc,
12};
13
14pub const DEFAULT_CODE: &str = "nu::shell::error";
16
17#[non_exhaustive]
28#[derive(Debug, Clone, PartialEq)]
29pub struct GenericError {
30 pub code: Cow<'static, str>,
35
36 pub error: Cow<'static, str>,
38
39 pub msg: Cow<'static, str>,
41
42 pub site: ErrorSite,
44
45 pub help: Option<Cow<'static, str>>,
47
48 pub inner: Vec<ShellError>,
50
51 pub source: Option<ErrorSource>,
53}
54
55impl GenericError {
56 #[track_caller]
61 pub fn new(
62 error: impl Into<Cow<'static, str>>,
63 msg: impl Into<Cow<'static, str>>,
64 span: Span,
65 ) -> Self {
66 Self {
74 code: DEFAULT_CODE.into(),
75 error: error.into(),
76 msg: msg.into(),
77 site: ErrorSite::Span(span),
78 help: None,
79 inner: Vec::new(),
80 source: None,
81 }
82 }
83
84 #[track_caller]
89 pub fn new_internal(
90 error: impl Into<Cow<'static, str>>,
91 msg: impl Into<Cow<'static, str>>,
92 ) -> Self {
93 let location = Location::caller();
94 Self {
95 code: DEFAULT_CODE.into(),
96 error: error.into(),
97 msg: msg.into(),
98 site: ErrorSite::Location(location.to_string()),
99 help: None,
100 inner: Vec::new(),
101 source: None,
102 }
103 }
104
105 pub fn new_internal_with_location(
111 error: impl Into<Cow<'static, str>>,
112 msg: impl Into<Cow<'static, str>>,
113 location: impl Into<Location>,
114 ) -> Self {
115 Self {
116 code: DEFAULT_CODE.into(),
117 error: error.into(),
118 msg: msg.into(),
119 site: ErrorSite::Location(location.into().to_string()),
120 help: None,
121 inner: Vec::new(),
122 source: None,
123 }
124 }
125
126 pub fn with_code(self, code: impl Into<Cow<'static, str>>) -> Self {
128 Self {
129 code: code.into(),
130 ..self
131 }
132 }
133
134 pub fn with_help(self, help: impl Into<Cow<'static, str>>) -> Self {
136 Self {
137 help: Some(help.into()),
138 ..self
139 }
140 }
141
142 pub fn with_inner(self, inner: impl IntoIterator<Item = ShellError>) -> Self {
144 Self {
145 inner: inner.into_iter().collect(),
146 ..self
147 }
148 }
149
150 pub fn with_source(self, source: impl StdError + Send + Sync + 'static) -> Self {
152 Self {
153 source: Some(ErrorSource(Arc::new(source))),
154 ..self
155 }
156 }
157}
158
159impl Display for GenericError {
160 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161 let GenericError { error, .. } = self;
162 write!(f, "{error}")
163 }
164}
165
166impl StdError for GenericError {}
167
168impl Diagnostic for GenericError {
169 fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
170 Some(Box::new(self.code.as_ref()))
171 }
172
173 fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
174 let span = match &self.site {
175 ErrorSite::Span(span) => (*span).into(),
176 ErrorSite::Location(location) => miette::SourceSpan::new(0.into(), location.len()),
177 };
178
179 let label = miette::LabeledSpan::new_with_span(Some(self.msg.to_string()), span);
180 Some(Box::new(std::iter::once(label)))
181 }
182
183 fn source_code(&self) -> Option<&dyn miette::SourceCode> {
184 match &self.site {
185 ErrorSite::Span(_) => None,
186 ErrorSite::Location(location) => Some(location as &dyn miette::SourceCode),
187 }
188 }
189
190 fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
191 self.help
192 .as_ref()
193 .map(|help| Box::new(help.as_ref()) as Box<dyn Display>)
194 }
195
196 fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
197 match &self.inner.is_empty() {
198 true => None,
199 false => Some(Box::new(
200 self.inner.iter().map(|err| err as &dyn Diagnostic),
201 )),
202 }
203 }
204
205 fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
206 self.source.as_ref().map(|err| err as &dyn Diagnostic)
207 }
208}