rune/workspace/
error.rs

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/// An error raised when interacting with workspaces.
13#[derive(Debug)]
14pub struct WorkspaceError {
15    span: Span,
16    kind: rust_alloc::boxed::Box<WorkspaceErrorKind>,
17}
18
19impl WorkspaceError {
20    /// Construct a new workspace error with the given span and kind.
21    #[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    /// Construct a custom message as an error.
34    #[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/// A workspace error.
94#[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}