Skip to main content

panproto_schema/
error.rs

1//! Error types for schema operations.
2
3use std::fmt;
4
5/// Errors that can occur during schema construction and validation.
6#[derive(Debug, thiserror::Error)]
7#[non_exhaustive]
8pub enum SchemaError {
9    /// A vertex ID was referenced but not found in the schema.
10    #[error("vertex not found: {0}")]
11    VertexNotFound(String),
12
13    /// A duplicate vertex ID was added to the schema.
14    #[error("duplicate vertex id: {0}")]
15    DuplicateVertex(String),
16
17    /// A duplicate edge was added to the schema.
18    #[error("duplicate edge from {src} to {tgt} of kind {kind}")]
19    DuplicateEdge {
20        /// Source vertex ID.
21        src: String,
22        /// Target vertex ID.
23        tgt: String,
24        /// Edge kind.
25        kind: String,
26    },
27
28    /// A duplicate hyper-edge ID was added to the schema.
29    #[error("duplicate hyper-edge id: {0}")]
30    DuplicateHyperEdge(String),
31
32    /// An edge kind violates the protocol's edge rules.
33    #[error(
34        "invalid edge kind {kind}: source kind {src_kind} not allowed (permitted: {permitted})"
35    )]
36    InvalidEdgeSource {
37        /// The edge kind.
38        kind: String,
39        /// The actual source vertex kind.
40        src_kind: String,
41        /// Comma-separated list of permitted source kinds.
42        permitted: String,
43    },
44
45    /// An edge kind violates the protocol's edge rules (target).
46    #[error(
47        "invalid edge kind {kind}: target kind {tgt_kind} not allowed (permitted: {permitted})"
48    )]
49    InvalidEdgeTarget {
50        /// The edge kind.
51        kind: String,
52        /// The actual target vertex kind.
53        tgt_kind: String,
54        /// Comma-separated list of permitted target kinds.
55        permitted: String,
56    },
57
58    /// An edge kind is not recognized by the protocol.
59    #[error("unknown edge kind: {0}")]
60    UnknownEdgeKind(String),
61
62    /// A vertex kind is not recognized by the protocol's schema theory.
63    #[error("unknown vertex kind: {0}")]
64    UnknownVertexKind(String),
65
66    /// The schema has no vertices.
67    #[error("schema has no vertices")]
68    EmptySchema,
69
70    /// A pushout overlap identified an edge that the schema it is drawn from
71    /// does not contain, so the identification has no endpoints to close over.
72    #[error(
73        "overlap names a {side} edge from {src} to {tgt} of kind {kind} that is not in that schema"
74    )]
75    OverlapEdgeNotFound {
76        /// Which schema the edge was drawn from: `left` or `right`.
77        side: &'static str,
78        /// Source vertex ID.
79        src: String,
80        /// Target vertex ID.
81        tgt: String,
82        /// Edge kind.
83        kind: String,
84    },
85
86    /// A declared entry vertex does not exist in the schema.
87    #[error("entry vertex not found: {0}")]
88    UnknownEntryVertex(String),
89
90    /// `SchemaBuilder::build_abstract` was called on a builder that
91    /// has accumulated constraints in the layout enrichment fibre.
92    /// Abstract schemas must carry no layout witnesses; call
93    /// `build_decorated` if a decorated schema was intended.
94    #[error(
95        "build_abstract called on a builder with layout-fibre constraints; \
96         use build_decorated for a decorated schema"
97    )]
98    LayoutConstraintsOnAbstractBuild,
99
100    /// [`induce`](crate::induce()) produced a sub-schema that fails
101    /// [`validate`](crate::validate) against the protocol it was cut from.
102    ///
103    /// Because induction never invents a vertex, an edge, a kind or a
104    /// constraint, every finding here is inherited from the parent schema:
105    /// either the parent was already invalid, or the cut exposed a
106    /// requirement whose endpoints did not survive.
107    #[error(
108        "induced sub-schema is invalid: {}",
109        .findings.iter().map(ToString::to_string).collect::<Vec<_>>().join("; ")
110    )]
111    InducedSchemaInvalid {
112        /// Every violation reported for the induced sub-schema.
113        findings: Vec<ValidationError>,
114    },
115}
116
117/// An error found during schema validation against a protocol.
118#[derive(Debug, Clone, PartialEq, Eq)]
119#[non_exhaustive]
120pub enum ValidationError {
121    /// An edge violates the protocol's edge rules.
122    InvalidEdge {
123        /// The offending edge's source vertex ID.
124        src: String,
125        /// The offending edge's target vertex ID.
126        tgt: String,
127        /// The edge kind.
128        kind: String,
129        /// Human-readable reason for the violation.
130        reason: String,
131    },
132
133    /// A constraint uses a sort not recognized by the protocol.
134    InvalidConstraintSort {
135        /// The vertex with the invalid constraint.
136        vertex: String,
137        /// The unrecognized sort.
138        sort: String,
139    },
140
141    /// A vertex kind is not recognized by the protocol.
142    InvalidVertexKind {
143        /// The vertex ID.
144        vertex: String,
145        /// The unrecognized kind.
146        kind: String,
147    },
148
149    /// A required edge references a missing vertex.
150    DanglingRequiredEdge {
151        /// The vertex ID.
152        vertex: String,
153        /// The dangling edge description.
154        edge: String,
155    },
156
157    /// A recursion point names a vertex the schema does not have.
158    ///
159    /// Either end can dangle: the marker itself, which is the key the point is
160    /// filed under, or the vertex it unfolds to. Neither is caught anywhere
161    /// else, and inducing a sub-schema silently drops a marker whose ends it
162    /// cannot find, so without this check a schema carrying one validates
163    /// clean and then loses the marker with no diagnostic.
164    DanglingRecursionPoint {
165        /// The marker vertex, which is the key in `recursion_points`.
166        mu: String,
167        /// Which end is missing, for the message.
168        missing: String,
169    },
170}
171
172impl fmt::Display for ValidationError {
173    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174        match self {
175            Self::InvalidEdge {
176                src,
177                tgt,
178                kind,
179                reason,
180            } => write!(f, "invalid edge {src} -> {tgt} ({kind}): {reason}"),
181            Self::InvalidConstraintSort { vertex, sort } => {
182                write!(f, "vertex {vertex} has invalid constraint sort: {sort}")
183            }
184            Self::InvalidVertexKind { vertex, kind } => {
185                write!(f, "vertex {vertex} has invalid kind: {kind}")
186            }
187            Self::DanglingRequiredEdge { vertex, edge } => {
188                write!(f, "vertex {vertex} has dangling required edge: {edge}")
189            }
190            Self::DanglingRecursionPoint { mu, missing } => {
191                write!(
192                    f,
193                    "recursion point {mu} names a vertex the schema does not have: {missing}"
194                )
195            }
196        }
197    }
198}