1#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
5pub enum Error {
6 #[error("unsupported format_version {found} (supported: {supported})")]
7 UnsupportedVersion { found: u32, supported: u32 },
8 #[error("duplicate name `{name}`")]
9 DuplicateName { name: String },
10 #[error("node `{node}` references unknown value `{name}`")]
11 UnknownReference { node: String, name: String },
12 #[error("output `{name}` does not exist")]
13 UnknownOutput { name: String },
14 #[error("graph contains a cycle through node `{node}`")]
15 Cycle { node: String },
16 #[error("node `{node}`: {op} takes {expected} input(s), got {found}")]
17 Arity {
18 node: String,
19 op: &'static str,
20 expected: usize,
21 found: usize,
22 },
23 #[error("node `{node}`: unsupported op `{op}`")]
24 UnsupportedOp { node: String, op: String },
25 #[error("`{name}`: shape {shape:?} needs {expected} value(s), got {found}")]
26 DataLength {
27 name: String,
28 shape: Vec<usize>,
29 expected: usize,
30 found: usize,
31 },
32 #[error("`{name}`: invalid shape {shape:?}: {reason}")]
33 InvalidShape {
34 name: String,
35 shape: Vec<usize>,
36 reason: &'static str,
37 },
38 #[error("limit exceeded: {what} is {found}, maximum is {limit}")]
39 LimitExceeded {
40 what: &'static str,
41 found: usize,
42 limit: usize,
43 },
44 #[error("`{name}`: {what} is {found}, maximum is {limit}")]
45 TensorLimit {
46 name: String,
47 what: &'static str,
48 found: usize,
49 limit: usize,
50 },
51 #[error("graph declares no outputs")]
52 NoOutputs,
53 #[error("`{name}` contains a non-finite value (NaN or infinity), which JSON cannot represent")]
54 NonFinite { name: String },
55 #[error("expected {expected} input tensor(s), got {found}")]
56 InputCount { expected: usize, found: usize },
57 #[error("input `{name}`: expected shape {expected:?}, got {found:?}")]
58 InputShape {
59 name: String,
60 expected: Vec<usize>,
61 found: Vec<usize>,
62 },
63 #[error("node `{node}`: {reason}")]
64 ShapeMismatch { node: String, reason: String },
65 #[error("node `{node}`: {op} requires attribute `{attr}`")]
66 MissingAttribute {
67 node: String,
68 op: &'static str,
69 attr: &'static str,
70 },
71 #[error("node `{node}`: {op} does not take attribute `{attr}`")]
72 UnexpectedAttribute {
73 node: String,
74 op: &'static str,
75 attr: &'static str,
76 },
77 #[error("node `{node}`: {op} attribute `{attr}`: {reason}")]
78 InvalidAttribute {
79 node: String,
80 op: &'static str,
81 attr: &'static str,
82 reason: String,
83 },
84 #[error("invalid graph: {0}")]
85 Verify(String),
86}
87
88impl Error {
89 pub fn code(&self) -> &'static str {
90 match self {
91 Error::UnsupportedVersion { .. } => "E001",
92 Error::DuplicateName { .. } => "E002",
93 Error::UnknownReference { .. } => "E003",
94 Error::UnknownOutput { .. } => "E004",
95 Error::Cycle { .. } => "E005",
96 Error::Arity { .. } => "E006",
97 Error::UnsupportedOp { .. } => "E007",
98 Error::DataLength { .. } => "E008",
99 Error::InvalidShape { .. } => "E009",
100 Error::LimitExceeded { .. } => "E010",
101 Error::NoOutputs => "E011",
102 Error::NonFinite { .. } => "E012",
103 Error::InputCount { .. } => "E013",
104 Error::InputShape { .. } => "E014",
105 Error::Verify(_) => "E015",
106 Error::TensorLimit { .. } => "E016",
107 Error::ShapeMismatch { .. } => "E017",
108 Error::MissingAttribute { .. } => "E018",
109 Error::UnexpectedAttribute { .. } => "E019",
110 Error::InvalidAttribute { .. } => "E020",
111 }
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118 use std::collections::HashSet;
119
120 fn one_of_each() -> Vec<Error> {
121 vec![
122 Error::UnsupportedVersion {
123 found: 2,
124 supported: 1,
125 },
126 Error::DuplicateName { name: "a".into() },
127 Error::UnknownReference {
128 node: "n".into(),
129 name: "a".into(),
130 },
131 Error::UnknownOutput { name: "a".into() },
132 Error::Cycle { node: "a".into() },
133 Error::Arity {
134 node: "n".into(),
135 op: "Relu",
136 expected: 1,
137 found: 2,
138 },
139 Error::UnsupportedOp {
140 node: "n".into(),
141 op: "Gelu".into(),
142 },
143 Error::DataLength {
144 name: "c".into(),
145 shape: vec![2],
146 expected: 2,
147 found: 1,
148 },
149 Error::InvalidShape {
150 name: "x".into(),
151 shape: vec![0],
152 reason: "dimensions must be at least 1",
153 },
154 Error::LimitExceeded {
155 what: "values",
156 found: 3,
157 limit: 2,
158 },
159 Error::NoOutputs,
160 Error::NonFinite { name: "c".into() },
161 Error::InputCount {
162 expected: 1,
163 found: 0,
164 },
165 Error::InputShape {
166 name: "x".into(),
167 expected: vec![2],
168 found: vec![3],
169 },
170 Error::TensorLimit {
171 name: "x".into(),
172 what: "tensor rank",
173 found: 9,
174 limit: 8,
175 },
176 Error::ShapeMismatch {
177 node: "n".into(),
178 reason: "r".into(),
179 },
180 Error::MissingAttribute {
181 node: "n".into(),
182 op: "Reshape",
183 attr: "shape",
184 },
185 Error::UnexpectedAttribute {
186 node: "n".into(),
187 op: "Relu",
188 attr: "shape",
189 },
190 Error::InvalidAttribute {
191 node: "n".into(),
192 op: "Reshape",
193 attr: "shape",
194 reason: "r".into(),
195 },
196 Error::Verify("bad".into()),
197 ]
198 }
199
200 #[test]
201 fn codes_are_unique_and_stable_format() {
202 let errors = one_of_each();
203 let codes: HashSet<&str> = errors.iter().map(Error::code).collect();
204 assert_eq!(codes.len(), errors.len());
205 assert!(codes.iter().all(|c| c.len() == 4 && c.starts_with('E')));
206 }
207
208 #[test]
209 fn messages_name_the_offender() {
210 assert_eq!(
211 Error::Cycle { node: "a".into() }.to_string(),
212 "graph contains a cycle through node `a`"
213 );
214 assert_eq!(
215 Error::UnknownReference {
216 node: "n".into(),
217 name: "q".into()
218 }
219 .to_string(),
220 "node `n` references unknown value `q`"
221 );
222 }
223}