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}