1use core::fmt;
2
3use std::path::Path;
4
5use crate::alloc::{self, Box, String};
6use crate::ast::{Span, Spanned};
7use crate::compile::HasSpan;
8use crate::source;
9use crate::workspace::glob;
10use crate::SourceId;
11
12#[derive(Debug)]
14pub struct WorkspaceError {
15 span: Span,
16 kind: rust_alloc::boxed::Box<WorkspaceErrorKind>,
17}
18
19impl WorkspaceError {
20 #[inline]
22 pub(crate) fn new<S, K>(spanned: S, kind: K) -> Self
23 where
24 S: Spanned,
25 WorkspaceErrorKind: From<K>,
26 {
27 Self {
28 span: spanned.span(),
29 kind: rust_alloc::boxed::Box::new(WorkspaceErrorKind::from(kind)),
30 }
31 }
32
33 #[inline]
35 pub fn msg<S, M>(spanned: S, message: M) -> Self
36 where
37 S: Spanned,
38 M: fmt::Display + fmt::Debug + Send + Sync + 'static,
39 {
40 Self::new(
41 spanned,
42 WorkspaceErrorKind::Custom {
43 error: anyhow::Error::msg(message),
44 },
45 )
46 }
47}
48
49impl Spanned for WorkspaceError {
50 #[inline]
51 fn span(&self) -> Span {
52 self.span
53 }
54}
55
56impl core::error::Error for WorkspaceError {
57 #[inline]
58 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
59 self.kind.source()
60 }
61}
62
63impl fmt::Display for WorkspaceError {
64 #[inline]
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 fmt::Display::fmt(&self.kind, f)
67 }
68}
69
70impl WorkspaceError {
71 #[inline]
72 pub(crate) fn missing_field(span: Span, field: &'static str) -> Self {
73 Self::new(span, WorkspaceErrorKind::MissingField { field })
74 }
75
76 #[inline]
77 pub(crate) fn expected_array(span: Span) -> Self {
78 Self::new(span, WorkspaceErrorKind::ExpectedArray)
79 }
80}
81
82impl<S, E> From<HasSpan<S, E>> for WorkspaceError
83where
84 S: Spanned,
85 WorkspaceErrorKind: From<E>,
86{
87 #[inline]
88 fn from(spanned: HasSpan<S, E>) -> Self {
89 Self::new(spanned.span(), spanned.into_inner())
90 }
91}
92
93#[derive(Debug)]
95#[allow(missing_docs)]
96#[non_exhaustive]
97pub(crate) enum WorkspaceErrorKind {
98 Custom {
99 error: anyhow::Error,
100 },
101 GlobError {
102 path: Box<Path>,
103 error: glob::GlobError,
104 },
105 Source {
106 path: Box<Path>,
107 error: source::FromPathError,
108 },
109 Toml {
110 error: toml::de::Error,
111 },
112 Key {
113 error: serde_hashkey::Error,
114 },
115 MissingSourceId {
116 source_id: SourceId,
117 },
118 MissingField {
119 field: &'static str,
120 },
121 ExpectedArray,
122 MissingManifestPath,
123 ExpectedTable,
124 UnsupportedKey {
125 key: String,
126 },
127 AllocError {
128 error: alloc::Error,
129 },
130}
131
132impl core::error::Error for WorkspaceErrorKind {
133 #[inline]
134 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
135 match self {
136 WorkspaceErrorKind::GlobError { error, .. } => Some(error),
137 WorkspaceErrorKind::Source { error, .. } => Some(error),
138 WorkspaceErrorKind::Toml { error, .. } => Some(error),
139 WorkspaceErrorKind::Key { error, .. } => Some(error),
140 _ => None,
141 }
142 }
143}
144
145impl fmt::Display for WorkspaceErrorKind {
146 #[inline]
147 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
148 match self {
149 WorkspaceErrorKind::Custom { error } => error.fmt(f),
150 WorkspaceErrorKind::GlobError { path, error } => write!(
151 f,
152 "Failed to glob at `{path}`: {error}",
153 path = path.display()
154 ),
155 WorkspaceErrorKind::Source { path, error } => write!(
156 f,
157 "Failed to load source at `{path}`: {error}",
158 path = path.display()
159 ),
160 WorkspaceErrorKind::Toml { error } => {
161 write!(f, "Failed to deserialize manifest: {error}",)
162 }
163 WorkspaceErrorKind::Key { error } => {
164 write!(f, "Failed to deserialize: {error}")
165 }
166 WorkspaceErrorKind::MissingSourceId { source_id } => {
167 write!(f, "Missing source id `{source_id}`",)
168 }
169 WorkspaceErrorKind::MissingField { field } => {
170 write!(f, "Missing required field `{field}`",)
171 }
172 WorkspaceErrorKind::ExpectedArray => write!(f, "Expected array"),
173 WorkspaceErrorKind::MissingManifestPath => write!(
174 f,
175 "Element `[workspace]` can only be used in manifests with a valid path"
176 ),
177 WorkspaceErrorKind::ExpectedTable => write!(f, "Expected table"),
178 WorkspaceErrorKind::UnsupportedKey { key } => write!(f, "Key `{key}` not supported",),
179 WorkspaceErrorKind::AllocError { error } => error.fmt(f),
180 }
181 }
182}
183
184impl From<anyhow::Error> for WorkspaceErrorKind {
185 #[inline]
186 fn from(error: anyhow::Error) -> Self {
187 WorkspaceErrorKind::Custom { error }
188 }
189}
190
191impl From<toml::de::Error> for WorkspaceErrorKind {
192 #[inline]
193 fn from(error: toml::de::Error) -> Self {
194 WorkspaceErrorKind::Toml { error }
195 }
196}
197
198impl From<serde_hashkey::Error> for WorkspaceErrorKind {
199 #[inline]
200 fn from(error: serde_hashkey::Error) -> Self {
201 WorkspaceErrorKind::Key { error }
202 }
203}
204
205impl From<alloc::Error> for WorkspaceError {
206 #[inline]
207 fn from(error: alloc::Error) -> Self {
208 WorkspaceError::new(Span::empty(), error)
209 }
210}
211
212impl From<alloc::Error> for WorkspaceErrorKind {
213 #[inline]
214 fn from(error: alloc::Error) -> Self {
215 WorkspaceErrorKind::AllocError { error }
216 }
217}