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}