1use crate::node::NodeId;
7use crate::version::IrVersion;
8
9#[derive(Debug, thiserror::Error)]
14#[non_exhaustive]
15pub enum InputValidationError {
16 #[error("StyledTree must contain at least one node")]
18 EmptyTree,
19
20 #[error(
22 "IR version {tree_version} is incompatible with engine version {engine_version} \
23 (same major version required, tree minor <= engine minor)"
24 )]
25 IncompatibleVersion {
26 tree_version: IrVersion,
27 engine_version: IrVersion,
28 },
29
30 #[error(
32 "{parent} references child {child} which does not exist \
33 (tree has {node_count} nodes)"
34 )]
35 InvalidChildReference {
36 parent: NodeId,
37 child: NodeId,
38 node_count: u32,
39 },
40
41 #[error("{child} is a child of both {parent_a} and {parent_b}")]
44 MultipleParents {
45 child: NodeId,
46 parent_a: NodeId,
47 parent_b: NodeId,
48 },
49
50 #[error("{node} references itself as a child")]
52 SelfReference { node: NodeId },
53
54 #[error("{node} is not reachable from the root")]
56 OrphanNode { node: NodeId },
57
58 #[error("resource limit exceeded: {limit_name} (current: {current}, max: {max})")]
61 ResourceLimitExceeded {
62 limit_name: &'static str,
64 current: u64,
66 max: u64,
68 node_path: Vec<NodeId>,
71 },
72
73 #[error("invalid page template: {detail}")]
76 InvalidPageTemplate { detail: String },
77
78 #[error("unsupported feature \"{feature_name}\" at {node_id}: {detail}")]
82 UnsupportedFeature {
83 node_id: NodeId,
85 feature_name: String,
87 detail: String,
89 },
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 #[test]
97 fn error_display_messages() {
98 let err = InputValidationError::EmptyTree;
99 assert_eq!(err.to_string(), "StyledTree must contain at least one node");
100
101 let err = InputValidationError::IncompatibleVersion {
102 tree_version: IrVersion::new(2, 0),
103 engine_version: IrVersion::new(1, 0),
104 };
105 assert!(err.to_string().contains("2.0"));
106 assert!(err.to_string().contains("1.0"));
107
108 let err = InputValidationError::ResourceLimitExceeded {
109 limit_name: "node_count",
110 current: 1_500_000,
111 max: 1_000_000,
112 node_path: vec![],
113 };
114 assert!(err.to_string().contains("node_count"));
115 assert!(err.to_string().contains("1500000"));
116 assert!(err.to_string().contains("1000000"));
117 }
118
119 #[test]
120 fn unsupported_feature_display() {
121 let err = InputValidationError::UnsupportedFeature {
122 node_id: NodeId::from_raw(7),
123 feature_name: "cmyk_color".into(),
124 detail: "CMYK color space is not supported".into(),
125 };
126 let msg = err.to_string();
127 assert!(msg.contains("cmyk_color"));
128 assert!(msg.contains("node#7"));
129 assert!(msg.contains("CMYK"));
130 }
131
132 #[test]
133 fn resource_limit_with_node_path() {
134 let err = InputValidationError::ResourceLimitExceeded {
135 limit_name: "tree_depth",
136 current: 300,
137 max: 256,
138 node_path: vec![
139 NodeId::from_raw(0),
140 NodeId::from_raw(3),
141 NodeId::from_raw(12),
142 ],
143 };
144 assert!(err.to_string().contains("tree_depth"));
145 assert_eq!(
146 match &err {
147 InputValidationError::ResourceLimitExceeded { node_path, .. } => node_path.len(),
148 _ => 0,
149 },
150 3
151 );
152 }
153
154 #[test]
155 fn error_is_std_error() {
156 let err: Box<dyn std::error::Error> = Box::new(InputValidationError::EmptyTree);
157 assert!(!err.to_string().is_empty());
159 }
160}