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