Skip to main content

webui/
error.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT license.
3
4//! Error types for the WebUI library.
5
6use thiserror::Error;
7
8/// Errors that can occur during WebUI build, render, or inspection operations.
9#[derive(Debug, Error)]
10pub enum WebUIError {
11    /// Transactional component asset publication or cleanup failure.
12    #[error("{context}")]
13    ComponentAssetPublication {
14        /// The failed publication operation.
15        context: String,
16        /// The underlying filesystem, serialization, or finalization error.
17        #[source]
18        source: Box<dyn std::error::Error + Send + Sync>,
19    },
20    /// I/O error (file read/write failures).
21    #[error("I/O error: {context}")]
22    Io {
23        /// What operation failed.
24        context: String,
25        /// The underlying I/O error.
26        #[source]
27        source: std::io::Error,
28    },
29
30    /// Component registration failure while reading, transforming, or
31    /// inserting an authored component source.
32    ///
33    /// `context` names what was being registered (an app directory or a
34    /// discovered source). The underlying [`ParserError`] is preserved as a
35    /// structured `#[source]` rather than being flattened to a string, so a
36    /// build-time authoring mistake surfaced by a component-source transform
37    /// reaches the CLI as [`ParserError::Template`] with its stable diagnostic
38    /// `code`, source location, snippet, and `help` intact — the same
39    /// structured path already used by [`WebUIError::Parse`].
40    ///
41    /// [`ParserError`]: webui_parser::ParserError
42    /// [`ParserError::Template`]: webui_parser::ParserError::Template
43    #[error("{context}")]
44    ComponentRegistration {
45        /// What was being registered (path or discovered source).
46        context: String,
47        /// The underlying registration error, structurally preserved.
48        #[source]
49        source: webui_parser::ParserError,
50    },
51
52    /// Component discovery failure (npm packages or local paths).
53    #[error("Component discovery error: {0}")]
54    ComponentDiscovery(String),
55
56    /// HTML/CSS parsing failure with context about which file failed.
57    #[error("{context}")]
58    Parse {
59        /// What was being parsed.
60        context: String,
61        /// The underlying parse error.
62        #[source]
63        source: webui_parser::ParserError,
64    },
65
66    /// Protocol serialization or deserialization failure.
67    #[error("protocol error")]
68    Protocol(#[from] webui_protocol::ProtocolError),
69
70    /// JSON serialization failure.
71    #[error("Serialization error: {0}")]
72    Serialization(String),
73
74    /// Handler rendering error.
75    #[error("rendering error")]
76    Rendering(#[from] webui_handler::HandlerError),
77
78    /// Invalid build-option configuration.
79    #[error("Invalid build options: {0}")]
80    InvalidBuildOptions(String),
81
82    /// Static component asset graph failure with a structured diagnostic.
83    #[error("{0}")]
84    ComponentAssets(Box<webui_parser::Diagnostic>),
85
86    /// Bundler-neutral state projection manifest failure: malformed schema,
87    /// stale/missing input or output file, build-ID mismatch, conflicting or
88    /// duplicate fragment ownership, missing scripted-component coverage, or
89    /// an incompatible plugin. Carries a structured, color-free [`Diagnostic`]
90    /// with a stable `PROJ-*` code (see `webui::projection::codes`).
91    ///
92    /// The diagnostic is boxed so this cold error path does not enlarge
93    /// `Result<_, WebUIError>` on the common success path.
94    #[error("{0}")]
95    Projection(Box<webui_parser::Diagnostic>),
96}
97
98impl WebUIError {
99    /// The full, single-line message including the source chain, e.g.
100    /// `Failed to parse index.html: Directive error: Invalid for each: x`.
101    ///
102    /// Each error layer's [`std::fmt::Display`] describes only its own level
103    /// (the `#[source]` chain carries the rest), so the CLI and dev server can
104    /// use anyhow's `{:#}` formatting without repetition. Hosts that surface a
105    /// flat string instead of walking the chain — Node, FFI — use this helper
106    /// to get the same complete message.
107    #[must_use]
108    pub fn chain_message(&self) -> String {
109        let mut message = self.to_string();
110        let mut source = std::error::Error::source(self);
111        while let Some(err) = source {
112            message.push_str(": ");
113            message.push_str(&err.to_string());
114            source = err.source();
115        }
116        message
117    }
118}