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