openusd_build/error.rs
1//! What generation can fail with.
2
3use std::io;
4use std::path::PathBuf;
5
6use crate::validate::Violation;
7
8/// What a configured [`TokenEnum`](crate::TokenEnum) asked for that could not
9/// be honoured.
10///
11/// Every one of these is a configuration mistake rather than a schema one, and
12/// each stops generation: an enum silently left out, or one quietly given a
13/// variant name nobody chose, is worse than a build that stops and says which.
14#[derive(Debug, thiserror::Error)]
15#[non_exhaustive]
16pub enum TokenEnumError {
17 /// A source not spelled `Class.property`.
18 #[error("names source `{spelling}`, which is not spelled `Class.property`")]
19 MalformedSource {
20 /// What was configured.
21 spelling: String,
22 },
23
24 /// A class the library does not declare.
25 #[error("names class `{class}`, which the library does not declare")]
26 UnknownClass {
27 /// The class named.
28 class: String,
29 },
30
31 /// A property the class does not declare.
32 #[error("names `{class}.{property}`, which the class does not declare")]
33 UnknownProperty {
34 /// The class that was found.
35 class: String,
36 /// The property that was not.
37 property: String,
38 },
39
40 /// A source property declaring no `allowedTokens`, so there are no variants
41 /// to generate.
42 #[error("sources `{class}.{property}`, which declares no allowedTokens")]
43 NoAllowedTokens {
44 /// The class holding the property.
45 class: String,
46 /// The property named.
47 property: String,
48 },
49
50 /// A default asked for from a property with no token fallback to take it
51 /// from.
52 #[error("asks for a default, but `{class}.{property}` declares no token fallback")]
53 NoFallback {
54 /// The class holding the property.
55 class: String,
56 /// The property named.
57 property: String,
58 },
59
60 /// A default asked for from a fallback the allowed tokens do not admit.
61 #[error("asks for a default of `{token}`, which is not among its allowed tokens")]
62 UnknownFallback {
63 /// The fallback the property declares.
64 token: String,
65 },
66
67 /// A token no casing rule turns into a Rust name — an empty spelling, one
68 /// opening with a digit, one that is punctuation alone.
69 #[error("has no Rust name for token `{token}`; give one with TokenEnum::variant")]
70 UnnameableToken {
71 /// The token that could not be named.
72 token: String,
73 },
74
75 /// Two tokens reaching one variant name. Suffixing one of them would put a
76 /// name nobody chose into a public API, so the caller is asked instead.
77 #[error("names both `{first}` and `{second}` `{variant}`; give one a name with TokenEnum::variant")]
78 VariantCollision {
79 /// The token that took the name.
80 first: String,
81 /// The token that wanted it too.
82 second: String,
83 /// The name they both reached.
84 variant: String,
85 },
86
87 /// A type name the caller spelled that Rust would not take.
88 #[error("is not a Rust identifier")]
89 InvalidName,
90
91 /// A type name the generated file already uses for something else.
92 #[error("takes the name of another item the generated file declares")]
93 ReservedName,
94
95 /// A library neither generated by this run nor declared with
96 /// [`extern_library`](crate::Builder::extern_library), which is a name
97 /// nothing will ever match.
98 #[error("names library `{library}`, which this build neither generates nor declares")]
99 UnknownLibrary {
100 /// The library named.
101 library: String,
102 },
103
104 /// A variant name the caller spelled that Rust would not take.
105 #[error("names token `{token}` `{variant}`, which is not a Rust identifier")]
106 InvalidVariant {
107 /// The token it was given for.
108 token: String,
109 /// The name that was given.
110 variant: String,
111 },
112
113 /// A variant override for a token the source property does not admit,
114 /// which is a name that would silently do nothing.
115 #[error("names a variant for token `{token}`, which is not among its allowed tokens")]
116 UnknownVariantToken {
117 /// The token the override named.
118 token: String,
119 },
120
121 /// Two enums of one library under one name.
122 #[error("is declared more than once")]
123 Duplicate,
124}
125
126/// A failure while generating a schema library.
127///
128/// A build script runs this, so a variant that has a file to name names it:
129/// the message is all a contributor sees when `cargo build` stops.
130#[derive(Debug, thiserror::Error)]
131#[non_exhaustive]
132pub enum Error {
133 /// Generation had schemas to build but nowhere to write them.
134 #[error("no output directory: call Builder::out_dir, or run this from a build script, which sets OUT_DIR")]
135 NoOutDir,
136
137 /// Reading a schema through `openusd` failed.
138 #[error(transparent)]
139 Core(#[from] openusd::Error),
140
141 /// A schema's layers did not compose, so what it declares cannot be read.
142 #[error("{schema} does not compose: {diagnostic}")]
143 Composition {
144 /// The schema that was being read.
145 schema: PathBuf,
146 /// What composition reported. An unresolved sublayer usually wants a
147 /// `search_path`.
148 diagnostic: String,
149 },
150
151 /// A schema library did not name itself, so nothing can refer to it.
152 #[error("{schema}: no /GLOBAL prim declares customData.libraryName")]
153 NoLibraryName {
154 /// The schema that was being read.
155 schema: PathBuf,
156 },
157
158 /// A schema declares something the generator cannot honour.
159 #[error("{origin}: {violation}")]
160 Definition {
161 /// Where the offending declaration lives, layer and path.
162 origin: String,
163 /// Which rule it broke.
164 violation: Violation,
165 },
166
167 /// A configured [`TokenEnum`](crate::TokenEnum) the schema cannot supply.
168 ///
169 /// Configuration is what asked for it, so this names the enum rather than a
170 /// layer: nothing in the schema is wrong, and the file to open is the build
171 /// script.
172 #[error("token enum `{name}` {cause}")]
173 TokenEnum {
174 /// The enum the configuration asked for.
175 name: String,
176 /// What it asked for that could not be honoured.
177 cause: TokenEnumError,
178 },
179
180 /// A field holds a value the generated declaration has no Rust literal for.
181 ///
182 /// Only a fallback or a piece of metadata reaches the table, so this is a
183 /// value type nothing generated has needed yet rather than an unusual
184 /// schema. It stops the build: a fallback that went missing would leave the
185 /// generated schemas answering differently from the ones they came from.
186 #[error("{schema}.{field} holds a {kind} value, which the generated declaration cannot express")]
187 UnwritableValue {
188 /// The schema declaring it.
189 schema: String,
190 /// The field on it.
191 field: String,
192 /// The value type that has no literal.
193 kind: &'static str,
194 },
195
196 /// The generator wrote Rust that does not parse.
197 ///
198 /// A defect in this crate rather than anything a schema can ask for, but it
199 /// surfaces in a consumer's build, so it says which schema was being
200 /// generated and what the parser made of it.
201 #[error("generated Rust for {schema} does not parse: {cause}")]
202 Malformed {
203 /// The schema being generated.
204 schema: String,
205 /// What parsing the emitted tokens reported.
206 cause: String,
207 },
208
209 /// Two schemas declare the same `libraryName`, so both would be written to
210 /// one `<library>.rs` and the second would silently replace the first.
211 #[error("{first} and {second} both declare libraryName `{library}`, so both would write {library}.rs")]
212 DuplicateLibrary {
213 /// The library name they share.
214 library: String,
215 /// The schema configured first.
216 first: PathBuf,
217 /// The schema configured second.
218 second: PathBuf,
219 },
220
221 /// A shader-definition layer does not declare its nodes in a form this
222 /// crate generates views of.
223 #[error("invalid shader definitions {path}: {cause}")]
224 ShaderDefs {
225 /// The layer.
226 path: PathBuf,
227 /// What is wrong with it.
228 cause: String,
229 },
230
231 /// A `plugInfo.json` does not declare its metadata fields or kinds in a
232 /// form this crate reads.
233 #[error("invalid plugInfo {path}: {cause}")]
234 PlugInfo {
235 /// The file.
236 path: PathBuf,
237 /// What is wrong with it.
238 cause: String,
239 },
240
241 /// A `plugInfo.json` declares a plugin that is neither a configured
242 /// schema's `libraryName` nor a requested declaration family, so what it
243 /// declares would reach no generated file.
244 #[error("{path} declares plugin `{plugin}`, which is neither a configured schema library nor a declaration family")]
245 UnknownPlugin {
246 /// The plugin name.
247 plugin: String,
248 /// The file declaring it.
249 path: PathBuf,
250 },
251
252 /// A declaration family's name is not an identifier, so it could not name
253 /// the file generated for it.
254 #[error("declaration family `{family}` is not a valid identifier")]
255 InvalidDeclarationFamily {
256 /// The requested family.
257 family: String,
258 },
259
260 /// A declaration family was requested for a plugin no configured
261 /// `plugInfo.json` declares.
262 #[error("no configured plugInfo declares a plugin called `{family}`, requested as a declaration family")]
263 UnknownDeclarationFamily {
264 /// The requested family.
265 family: String,
266 },
267
268 /// Two libraries declare one kind, so the registry would refuse the
269 /// second.
270 #[error("kind `{kind}` is declared by both {first} and {second}")]
271 DuplicateKind {
272 /// The kind.
273 kind: String,
274 /// The library declaring it first.
275 first: String,
276 /// The library declaring it again.
277 second: String,
278 },
279
280 /// Two libraries declare one metadata field, so the registry would refuse
281 /// the second.
282 #[error("metadata field `{field}` is declared by both {first} and {second}")]
283 DuplicateMetadataField {
284 /// The field.
285 field: String,
286 /// The library declaring it first.
287 first: String,
288 /// The library declaring it again.
289 second: String,
290 },
291
292 /// Two of a library's metadata fields would be read or written by one
293 /// method of the trait they share.
294 #[error("metadata fields `{first}` and `{second}` of {library} both need a method called `{method}`")]
295 MetadataMethodCollision {
296 /// The library declaring them.
297 library: String,
298 /// The method both need.
299 method: String,
300 /// The field named first.
301 first: String,
302 /// The field named second.
303 second: String,
304 },
305
306 /// A file or directory could not be read, written or created.
307 #[error("cannot access {path}")]
308 Io {
309 /// What was being read or written.
310 path: PathBuf,
311 /// What the filesystem reported.
312 #[source]
313 source: io::Error,
314 },
315}
316
317impl Error {
318 /// The first diagnostic `stage` raised, named against the schema it came
319 /// from.
320 ///
321 /// Composition reports many; the first is the one a contributor fixes,
322 /// and the rest usually follow from it.
323 pub(crate) fn composition(schema: &std::path::Path, stage: &openusd::usd::Stage) -> Option<Self> {
324 let diagnostic = stage.composition_errors().first()?.to_string();
325 Some(Error::Composition {
326 schema: schema.to_path_buf(),
327 diagnostic,
328 })
329 }
330}
331
332/// A malformed path names nothing to read.
333///
334/// Written out rather than derived: `#[from]` builds a conversion into the
335/// variant holding that type, and this belongs in [`Error::Core`] with every
336/// other core failure. Its own variant would let one failure arrive two ways —
337/// as itself, or nested in a `Core` — and leave a caller matching in two
338/// places for it. `openusd::Error` already tells them apart.
339impl From<openusd::sdf::PathParseError> for Error {
340 fn from(source: openusd::sdf::PathParseError) -> Self {
341 Error::Core(source.into())
342 }
343}
344
345/// Authoring the layers this crate writes goes through the same door every
346/// other edit does. Written out for the reason above.
347impl From<openusd::sdf::EditError> for Error {
348 fn from(source: openusd::sdf::EditError) -> Self {
349 Error::Core(source.into())
350 }
351}
352
353/// A composed query a reader asks of a stage. Written out for the reason
354/// above.
355impl From<openusd::pcp::QueryError> for Error {
356 fn from(source: openusd::pcp::QueryError) -> Self {
357 Error::Core(source.into())
358 }
359}
360
361/// A field that will not decode is schema data this crate cannot use. Written
362/// out for the reason above.
363impl From<openusd::sdf::DataError> for Error {
364 fn from(source: openusd::sdf::DataError) -> Self {
365 Error::Core(source.into())
366 }
367}