Skip to main content

golem_schema/schema/render/
error.rs

1// Copyright 2024-2026 Golem Cloud
2//
3// Licensed under the Golem Source License v1.1 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://license.golem.cloud/LICENSE
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Shared error type for the renderer family.
16
17use crate::schema::canonical::ParseError;
18use std::fmt::{Display, Formatter};
19
20/// Errors produced by the schema/value renderers.
21#[derive(Clone, Debug, PartialEq, Eq)]
22pub enum RenderError {
23    /// A [`crate::schema::SchemaValue`] did not match the shape required by
24    /// the accompanying [`crate::schema::SchemaType`].
25    ValueMismatch { path: String, reason: String },
26    /// A construct is recognised but not supported by the renderer (e.g.,
27    /// `Future` / `Stream` payloads).
28    Unsupported(&'static str),
29    /// A canonical scalar encoder or decoder rejected the input.
30    Canonical(ParseError),
31    /// A JSON conversion failed (e.g., a number out of range for the
32    /// target integer width).
33    Json(String),
34    /// A record decoder saw a JSON object with a field that is not declared
35    /// by the schema.
36    UnexpectedField { record: String, field: String },
37    /// A flags decoder saw the same flag name listed twice.
38    DuplicateFlag { flag: String },
39    /// A union encoder picked a branch by tag but the branch body did not
40    /// satisfy the branch's discriminator rule.
41    UnionTagMismatch { tag: String, reason: String },
42    /// A union decoder found multiple branches whose discriminator rules
43    /// match the same incoming value (a structural ambiguity that should
44    /// have been caught at validation; runtime is a safety net).
45    UnionAmbiguous { matched: Vec<String> },
46    /// A union decoder found no branch whose discriminator rule matches the
47    /// incoming value.
48    UnionNoMatch,
49}
50
51impl Display for RenderError {
52    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
53        match self {
54            RenderError::ValueMismatch { path, reason } => {
55                write!(f, "value mismatch at {path}: {reason}")
56            }
57            RenderError::Unsupported(what) => write!(f, "unsupported: {what}"),
58            RenderError::Canonical(inner) => write!(f, "canonical: {inner}"),
59            RenderError::Json(msg) => write!(f, "json: {msg}"),
60            RenderError::UnexpectedField { record, field } => {
61                write!(f, "unexpected field `{field}` on record `{record}`")
62            }
63            RenderError::DuplicateFlag { flag } => write!(f, "duplicate flag `{flag}`"),
64            RenderError::UnionTagMismatch { tag, reason } => {
65                write!(
66                    f,
67                    "union branch `{tag}` rejected by discriminator: {reason}"
68                )
69            }
70            RenderError::UnionAmbiguous { matched } => write!(
71                f,
72                "value matches multiple union branches: {}",
73                matched.join(", ")
74            ),
75            RenderError::UnionNoMatch => write!(f, "no union branch matched"),
76        }
77    }
78}
79
80impl std::error::Error for RenderError {}
81
82impl From<ParseError> for RenderError {
83    fn from(value: ParseError) -> Self {
84        RenderError::Canonical(value)
85    }
86}