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    #[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    /// Construct a custom message as an error.
34    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/// A workspace error.
89#[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}