Skip to main content

panproto_inst/
error.rs

1//! Error types for instance operations.
2
3use std::fmt;
4
5/// Errors from instance construction or manipulation.
6#[derive(Debug, thiserror::Error)]
7#[non_exhaustive]
8pub enum InstError {
9    /// A node ID was referenced but not found.
10    #[error("node not found: {0}")]
11    NodeNotFound(u32),
12
13    /// A vertex ID was referenced but not found in the schema.
14    #[error("vertex not found in schema: {0}")]
15    VertexNotFound(String),
16
17    /// The root node is missing.
18    #[error("root node missing from instance")]
19    MissingRoot,
20
21    /// An arc references a nonexistent node.
22    #[error("dangling arc: ({src}, {tgt})")]
23    DanglingArc {
24        /// Source node ID.
25        src: u32,
26        /// Target node ID.
27        tgt: u32,
28    },
29
30    /// Internal hom evaluation failed.
31    #[error("eval_hom failed: {0}")]
32    EvalHom(String),
33}
34
35/// Errors from the restrict operation.
36#[derive(Debug, thiserror::Error)]
37#[non_exhaustive]
38pub enum RestrictError {
39    /// No edge found between two vertices after ancestor contraction.
40    #[error("no edge found between {src} and {tgt} in target schema")]
41    NoEdgeFound {
42        /// Source vertex anchor.
43        src: String,
44        /// Target vertex anchor.
45        tgt: String,
46    },
47
48    /// Multiple edges found without a resolver entry.
49    #[error("ambiguous edge between {src} and {tgt}: {count} candidates")]
50    AmbiguousEdge {
51        /// Source vertex anchor.
52        src: String,
53        /// Target vertex anchor.
54        tgt: String,
55        /// Number of candidate edges.
56        count: usize,
57    },
58
59    /// The root was pruned during restriction.
60    #[error("root node was pruned during restriction")]
61    RootPruned,
62
63    /// Fan reconstruction failed.
64    #[error("fan reconstruction failed for hyper-edge {hyper_edge_id}: {detail}")]
65    FanReconstructionFailed {
66        /// The hyper-edge ID.
67        hyper_edge_id: String,
68        /// Details about the failure.
69        detail: String,
70    },
71
72    /// Cartesian product exceeded the configured size limit.
73    #[error("product size {actual} exceeds limit {limit} for vertex {vertex}")]
74    ProductSizeExceeded {
75        /// The target vertex whose fiber product is too large.
76        vertex: String,
77        /// The actual product size.
78        actual: usize,
79        /// The configured limit.
80        limit: usize,
81    },
82
83    /// Multi-element fiber encountered where only single-element fibers
84    /// are supported (e.g., W-type right Kan extension).
85    #[error("multi-element fiber for vertex {vertex}: {count} source vertices")]
86    MultiElementFiber {
87        /// The target vertex with multiple preimages.
88        vertex: String,
89        /// Number of source vertices in the fiber.
90        count: usize,
91    },
92}
93
94/// Errors from JSON parsing.
95#[derive(Debug, thiserror::Error)]
96#[non_exhaustive]
97pub enum ParseError {
98    /// The root vertex was not found in the schema.
99    #[error("root vertex not found in schema: {0}")]
100    RootVertexNotFound(String),
101
102    /// Expected a JSON object.
103    #[error("expected JSON object at path {path}")]
104    ExpectedObject {
105        /// JSON path where the error occurred.
106        path: String,
107    },
108
109    /// Expected a JSON array.
110    #[error("expected JSON array at path {path}")]
111    ExpectedArray {
112        /// JSON path where the error occurred.
113        path: String,
114    },
115
116    /// An edge references an unknown vertex kind.
117    #[error("unknown edge target at path {path}: {detail}")]
118    UnknownEdgeTarget {
119        /// JSON path where the error occurred.
120        path: String,
121        /// Details.
122        detail: String,
123    },
124
125    /// A value could not be parsed.
126    #[error("invalid value at path {path}: {detail}")]
127    InvalidValue {
128        /// JSON path where the error occurred.
129        path: String,
130        /// Details.
131        detail: String,
132    },
133
134    /// JSON structure error.
135    #[error("JSON error: {0}")]
136    Json(#[from] serde_json::Error),
137}
138
139/// A validation error found when checking a W-type instance against a schema.
140#[derive(Debug, Clone, PartialEq, Eq)]
141#[non_exhaustive]
142pub enum ValidationError {
143    /// A node's anchor vertex is not in the schema.
144    InvalidAnchor {
145        /// The offending node ID.
146        node_id: u32,
147        /// The anchor that was not found.
148        anchor: String,
149    },
150
151    /// An arc's edge is not in the schema.
152    InvalidEdge {
153        /// Parent node ID.
154        parent: u32,
155        /// Child node ID.
156        child: u32,
157        /// Details.
158        detail: String,
159    },
160
161    /// The root node is not present in the node set.
162    MissingRoot,
163
164    /// A child node is unreachable from the root.
165    UnreachableNode {
166        /// The unreachable node ID.
167        node_id: u32,
168    },
169
170    /// A required edge is missing from a node.
171    MissingRequiredEdge {
172        /// The node ID.
173        node_id: u32,
174        /// Description of the missing edge.
175        edge: String,
176    },
177
178    /// Parent map inconsistency.
179    ParentMapInconsistent {
180        /// The node with the inconsistency.
181        node_id: u32,
182        /// Details.
183        detail: String,
184    },
185
186    /// Fan references a nonexistent node.
187    InvalidFan {
188        /// The hyper-edge ID.
189        hyper_edge_id: String,
190        /// Details.
191        detail: String,
192    },
193}
194
195impl fmt::Display for ValidationError {
196    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197        match self {
198            Self::InvalidAnchor { node_id, anchor } => {
199                write!(f, "node {node_id} has invalid anchor: {anchor}")
200            }
201            Self::InvalidEdge {
202                parent,
203                child,
204                detail,
205            } => write!(f, "invalid edge ({parent}, {child}): {detail}"),
206            Self::MissingRoot => write!(f, "root node is missing"),
207            Self::UnreachableNode { node_id } => {
208                write!(f, "node {node_id} is unreachable from root")
209            }
210            Self::MissingRequiredEdge { node_id, edge } => {
211                write!(f, "node {node_id} missing required edge: {edge}")
212            }
213            Self::ParentMapInconsistent { node_id, detail } => {
214                write!(f, "parent map inconsistency at node {node_id}: {detail}")
215            }
216            Self::InvalidFan {
217                hyper_edge_id,
218                detail,
219            } => write!(f, "invalid fan for hyper-edge {hyper_edge_id}: {detail}"),
220        }
221    }
222}