Skip to main content

wdl_doc/
error.rs

1//! Error type definitions.
2
3use std::error::Error;
4use std::fmt::Display;
5use std::io::Error as IoError;
6use std::path::PathBuf;
7
8use wdl_analysis::AnalysisResult;
9
10/// Result type for documentation operations.
11pub type DocResult<T> = Result<T, DocError>;
12
13/// Extensions for documentation results.
14pub(crate) trait ResultContextExt {
15    /// Apply additional context to a [`DocResult`] if it contains an error.
16    fn with_context<F, C>(self, context: F) -> Self
17    where
18        F: FnOnce() -> C,
19        C: Display;
20}
21
22impl<T> ResultContextExt for DocResult<T> {
23    fn with_context<F, C>(self, context: F) -> Self
24    where
25        F: FnOnce() -> C,
26        C: Display,
27    {
28        self.map_err(|e| e.with_context(context()))
29    }
30}
31
32/// Errors that can occur while running `npm` commands.
33#[derive(Debug)]
34pub enum NpmError {
35    /// Failed to run `npm run build` in the theme directory.
36    Build(IoError),
37    /// Failed to run `npm install` in the theme directory.
38    Install(IoError),
39    /// Failed to run `npx pagefind` in the output directory.
40    SearchIndex(IoError),
41    /// Failed to run `npx @tailwindcss/cli`.
42    Tailwind(IoError),
43}
44
45impl Display for NpmError {
46    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47        match self {
48            NpmError::Build(e) => write!(f, "failed to run `npm run build`: {e}"),
49            NpmError::Install(e) => write!(f, "failed to run `npm install`: {e}"),
50            NpmError::SearchIndex(e) => write!(f, "failed to run `npx pagefind`: {e}"),
51            NpmError::Tailwind(e) => write!(f, "failed to run `npx @tailwindcss/cli`: {e}"),
52        }
53    }
54}
55
56/// The kinds of errors that can occur.
57#[derive(Debug)]
58pub enum DocErrorKind {
59    /// The expected workspace was not found.
60    WorkspaceNotFound(PathBuf),
61    /// No WDL documents were found in the workspace.
62    NoDocuments,
63    /// Failed to run analysis on the workspace.
64    Analyzer(anyhow::Error),
65    /// One or more documents failed analysis.
66    ///
67    /// This contains the analysis results of all failed documents.
68    AnalysisFailed(Vec<AnalysisResult>),
69    /// Failed to run an `npm` command.
70    Npm(NpmError),
71    /// An I/O operation failed.
72    Io(IoError),
73    /// A WDL module manifest could not be loaded.
74    Manifest(wdl_modules::manifest::ManifestError),
75}
76
77/// Errors that can occur while generating documentation.
78#[derive(Debug)]
79pub struct DocError {
80    /// An additional context message, if applicable.
81    context: Option<String>,
82    /// The kind of error that occurred.
83    kind: DocErrorKind,
84}
85
86impl DocError {
87    /// Create a new `DocError`.
88    pub fn new(kind: DocErrorKind) -> Self {
89        Self {
90            kind,
91            context: None,
92        }
93    }
94
95    /// The kind of error that occurred.
96    pub fn kind(&self) -> &DocErrorKind {
97        &self.kind
98    }
99
100    /// Add a context message to this error.
101    pub fn with_context(self, context: impl Display) -> Self {
102        Self {
103            context: Some(context.to_string()),
104            kind: self.kind,
105        }
106    }
107}
108
109impl Display for DocError {
110    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111        if let Some(ref ctx) = self.context {
112            write!(f, "{ctx}: ")?;
113        }
114
115        match &self.kind {
116            DocErrorKind::WorkspaceNotFound(root) => {
117                write!(
118                    f,
119                    "workspace root `{}` not found in analysis results",
120                    root.display()
121                )
122            }
123            DocErrorKind::NoDocuments => write!(f, "no WDL documents found in analysis"),
124            DocErrorKind::Analyzer(e) => write!(f, "{e}"),
125            DocErrorKind::AnalysisFailed(_) => {
126                write!(f, "a WDL document in the workspace has analysis errors")
127            }
128            DocErrorKind::Npm(e) => write!(f, "{e}"),
129            DocErrorKind::Io(e) => write!(f, "{e}"),
130            DocErrorKind::Manifest(e) => write!(f, "failed to load WDL module manifest: {e}"),
131        }
132    }
133}
134
135impl Error for DocError {
136    fn source(&self) -> Option<&(dyn Error + 'static)> {
137        match &self.kind {
138            DocErrorKind::Manifest(e) => Some(e),
139            _ => None,
140        }
141    }
142}
143
144impl From<NpmError> for DocError {
145    fn from(e: NpmError) -> Self {
146        DocError::new(DocErrorKind::Npm(e))
147    }
148}
149
150impl From<IoError> for DocError {
151    fn from(e: IoError) -> Self {
152        DocError::new(DocErrorKind::Io(e))
153    }
154}
155
156impl From<wdl_modules::manifest::ManifestError> for DocError {
157    fn from(e: wdl_modules::manifest::ManifestError) -> Self {
158        DocError::new(DocErrorKind::Manifest(e))
159    }
160}
161
162impl From<DocErrorKind> for DocError {
163    fn from(e: DocErrorKind) -> Self {
164        DocError::new(e)
165    }
166}