1use cm_types::ParseError;
6use fidl_fuchsia_component_decl as fdecl;
7use std::fmt;
8use std::fmt::Display;
9use thiserror::Error;
10
11#[derive(Debug, Error, PartialEq, Clone)]
13pub enum Error {
14 #[error("Field `{}` is missing for {}.", .0.field, .0.decl)]
15 MissingField(DeclField),
16
17 #[error("Field `{}` is empty for {}.", .0.field, .0.decl)]
18 EmptyField(DeclField),
19
20 #[error("{} has unnecessary field `{}`.", .0.decl, .0.field)]
21 ExtraneousField(DeclField),
22
23 #[error("\"{}\" is duplicated for field `{}` in {}.", .1, .0.field, .0.decl)]
24 DuplicateField(DeclField, String),
25
26 #[error("Field `{}` for {} is invalid.", .0.field, .0.decl)]
27 InvalidField(DeclField),
28
29 #[error("Field {} for {} is invalid. {}.", .0.field, .0.decl, .1)]
30 InvalidUrl(DeclField, String),
31
32 #[error("Field `{}` for {} is too long.", .0.field, .0.decl)]
33 FieldTooLong(DeclField),
34
35 #[error("Field `{}` for {} has an invalid path segment.", .0.field, .0.decl)]
36 FieldInvalidSegment(DeclField),
37
38 #[error("\"{0}\" capabilities must be offered as a built-in capability.")]
39 CapabilityMustBeBuiltin(DeclType),
40
41 #[error("\"{0}\" capabilities are not currently allowed as built-ins.")]
42 CapabilityCannotBeBuiltin(DeclType),
43
44 #[error(
45 "Encountered an unknown capability declaration. This may happen due to ABI skew between the FIDL component declaration and the system."
46 )]
47 UnknownCapability,
48
49 #[error("\"{1}\" is referenced in {0} but it does not appear in children.")]
50 InvalidChild(DeclField, String),
51
52 #[error("\"{1}\" is referenced in {0} but it does not appear in collections.")]
53 InvalidCollection(DeclField, String),
54
55 #[error("\"{1}\" is referenced in {0} but it does not appear in storage.")]
56 InvalidStorage(DeclField, String),
57
58 #[error("\"{1}\" is referenced in {0} but it does not appear in environments.")]
59 InvalidEnvironment(DeclField, String),
60
61 #[error("\"{1}\" is referenced in {0} but it does not appear in capabilities.")]
62 InvalidCapability(DeclField, String),
63
64 #[error("\"{1}\" is referenced in {0} but it does not appear in runners.")]
65 InvalidRunner(DeclField, String),
66
67 #[error("There are dependency cycle(s): {0}.")]
68 DependencyCycle(String),
69
70 #[error(
71 "Path \"{path}\" from {decl} overlaps with \"{other_path}\" path from {other_decl}. Paths across decls must be unique in order to avoid namespace collisions."
72 )]
73 InvalidPathOverlap { decl: DeclField, path: String, other_decl: DeclField, other_path: String },
74
75 #[error("{} \"{}\" path overlaps with \"/pkg\", which is a protected path", decl, path)]
76 PkgPathOverlap { decl: DeclField, path: String },
77
78 #[error(
79 "Source path \"{1}\" provided to {0} decl is unnecessary. Built-in capabilities don't need this field as they originate from the framework."
80 )]
81 ExtraneousSourcePath(DeclField, String),
82
83 #[error(
84 "Configuration schema defines a vector nested inside another vector. Vector can only contain numbers, booleans, and strings."
85 )]
86 NestedVector,
87
88 #[error(
89 "The `availability` field in {0} for {1} must be set to \"optional\" because the source is \"void\"."
90 )]
91 AvailabilityMustBeOptional(DeclField, String),
92
93 #[error("Invalid aggregate offer: {0}")]
94 InvalidAggregateOffer(String),
95
96 #[error(
97 "All sources that feed into an aggregation operation should have the same availability. Got {0}."
98 )]
99 DifferentAvailabilityInAggregation(AvailabilityList),
100
101 #[error("Multiple runners used.")]
102 MultipleRunnersUsed,
103
104 #[error("Used runner conflicts with program runner.")]
105 ConflictingRunners,
106
107 #[error(
108 "Runner is missing for executable component. A runner must be specified in the \
109 `program` section or in the `use` section."
110 )]
111 MissingRunner,
112
113 #[error("Dynamic children cannot specify an environment.")]
114 DynamicChildWithEnvironment,
115}
116
117#[derive(Debug, PartialEq, Clone)]
120pub struct AvailabilityList(pub Vec<fdecl::Availability>);
121
122impl Display for AvailabilityList {
123 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124 let comma_separated =
125 self.0.iter().map(|s| format!("{:?}", s)).collect::<Vec<_>>().join(", ");
126 write!(f, "[ {comma_separated} ]")
127 }
128}
129
130impl Error {
131 pub fn missing_field(decl_type: DeclType, keyword: impl Into<String>) -> Self {
132 Error::MissingField(DeclField { decl: decl_type, field: keyword.into() })
133 }
134
135 pub fn empty_field(decl_type: DeclType, keyword: impl Into<String>) -> Self {
136 Error::EmptyField(DeclField { decl: decl_type, field: keyword.into() })
137 }
138
139 pub fn extraneous_field(decl_type: DeclType, keyword: impl Into<String>) -> Self {
140 Error::ExtraneousField(DeclField { decl: decl_type, field: keyword.into() })
141 }
142
143 pub fn duplicate_field(
144 decl_type: DeclType,
145 keyword: impl Into<String>,
146 value: impl Into<String>,
147 ) -> Self {
148 Error::DuplicateField(DeclField { decl: decl_type, field: keyword.into() }, value.into())
149 }
150
151 pub fn invalid_field(decl_type: DeclType, keyword: impl Into<String>) -> Self {
152 Error::InvalidField(DeclField { decl: decl_type, field: keyword.into() })
153 }
154
155 pub fn invalid_url(
156 decl_type: DeclType,
157 keyword: impl Into<String>,
158 message: impl Into<String>,
159 ) -> Self {
160 Error::InvalidUrl(DeclField { decl: decl_type, field: keyword.into() }, message.into())
161 }
162
163 pub fn field_too_long(decl_type: DeclType, keyword: impl Into<String>) -> Self {
164 Error::FieldTooLong(DeclField { decl: decl_type, field: keyword.into() })
165 }
166
167 pub fn field_invalid_segment(decl_type: DeclType, keyword: impl Into<String>) -> Self {
168 Error::FieldInvalidSegment(DeclField { decl: decl_type, field: keyword.into() })
169 }
170
171 pub fn invalid_child(
172 decl_type: DeclType,
173 keyword: impl Into<String>,
174 child: impl Into<String>,
175 ) -> Self {
176 Error::InvalidChild(DeclField { decl: decl_type, field: keyword.into() }, child.into())
177 }
178
179 pub fn invalid_collection(
180 decl_type: DeclType,
181 keyword: impl Into<String>,
182 collection: impl Into<String>,
183 ) -> Self {
184 Error::InvalidCollection(
185 DeclField { decl: decl_type, field: keyword.into() },
186 collection.into(),
187 )
188 }
189
190 pub fn invalid_environment(
191 decl_type: DeclType,
192 keyword: impl Into<String>,
193 environment: impl Into<String>,
194 ) -> Self {
195 Error::InvalidEnvironment(
196 DeclField { decl: decl_type, field: keyword.into() },
197 environment.into(),
198 )
199 }
200
201 pub fn invalid_runner(
203 decl_type: DeclType,
204 keyword: impl Into<String>,
205 runner: impl Into<String>,
206 ) -> Self {
207 Error::InvalidRunner(DeclField { decl: decl_type, field: keyword.into() }, runner.into())
208 }
209
210 pub fn invalid_capability(
211 decl_type: DeclType,
212 keyword: impl Into<String>,
213 capability: impl Into<String>,
214 ) -> Self {
215 Error::InvalidCapability(
216 DeclField { decl: decl_type, field: keyword.into() },
217 capability.into(),
218 )
219 }
220
221 pub fn dependency_cycle(error: impl Into<String>) -> Self {
222 Error::DependencyCycle(error.into())
223 }
224
225 pub fn invalid_path_overlap(
226 decl: DeclType,
227 path: impl Into<String>,
228 other_decl: DeclType,
229 other_path: impl Into<String>,
230 ) -> Self {
231 Error::InvalidPathOverlap {
232 decl: DeclField { decl, field: "target_path".to_string() },
233 path: path.into(),
234 other_decl: DeclField { decl: other_decl, field: "target_path".to_string() },
235 other_path: other_path.into(),
236 }
237 }
238
239 pub fn pkg_path_overlap(decl: DeclType, path: impl Into<String>) -> Self {
240 Error::PkgPathOverlap {
241 decl: DeclField { decl, field: "target_path".to_string() },
242 path: path.into(),
243 }
244 }
245
246 pub fn extraneous_source_path(decl_type: DeclType, path: impl Into<String>) -> Self {
247 Error::ExtraneousSourcePath(
248 DeclField { decl: decl_type, field: "source_path".to_string() },
249 path.into(),
250 )
251 }
252
253 pub fn nested_vector() -> Self {
254 Error::NestedVector
255 }
256
257 pub fn availability_must_be_optional(
258 decl_type: DeclType,
259 keyword: impl Into<String>,
260 source_name: Option<&String>,
261 ) -> Self {
262 Error::AvailabilityMustBeOptional(
263 DeclField { decl: decl_type, field: keyword.into() },
264 source_name.cloned().unwrap_or_else(|| "<unnamed>".to_string()),
265 )
266 }
267
268 pub fn invalid_aggregate_offer(info: impl Into<String>) -> Self {
269 Error::InvalidAggregateOffer(info.into())
270 }
271
272 pub fn different_availability_in_aggregation(availability: Vec<fdecl::Availability>) -> Self {
273 Error::DifferentAvailabilityInAggregation(AvailabilityList(availability))
274 }
275
276 pub fn from_parse_error(
277 err: ParseError,
278 prop: &String,
279 decl_type: DeclType,
280 keyword: &str,
281 ) -> Self {
282 match err {
283 ParseError::Empty => Error::empty_field(decl_type, keyword),
284 ParseError::TooLong => Error::field_too_long(decl_type, keyword),
285 ParseError::InvalidComponentUrl { details } => {
286 Error::invalid_url(decl_type, keyword, format!(r#""{prop}": {details}"#))
287 }
288 ParseError::InvalidValue => Error::invalid_field(decl_type, keyword),
289 ParseError::InvalidSegment => Error::field_invalid_segment(decl_type, keyword),
290 ParseError::NoLeadingSlash => Error::invalid_field(decl_type, keyword),
291 }
292 }
293}
294
295#[derive(Debug, PartialEq, Clone, Copy)]
308pub enum DeclType {
309 AllowedOffers,
310 Availability,
311 Capability,
312 CapabilityRef,
313 Child,
314 ChildRef,
315 Collection,
316 CollectionRef,
317 Component,
318 Configuration,
319 ConfigChecksum,
320 ConfigField,
321 ConfigMutability,
322 ConfigOverride,
323 ConfigSchema,
324 ConfigSingleValue,
325 ConfigType,
326 ConfigTypeLayout,
327 ConfigValue,
328 ConfigValuesData,
329 ConfigValueSource,
330 ConfigValueSpec,
331 ConfigVectorValue,
332 DebugProtocolRegistration,
333 DebugRef,
334 DebugRegistration,
335 DependencyType,
336 Dictionary,
337 Directory,
338 Durability,
339 Environment,
340 EnvironmentExtends,
341 EventStream,
342 EventSubscription,
343 Expose,
344 ExposeConfig,
345 ExposeDictionary,
346 ExposeDirectory,
347 ExposeProtocol,
348 ExposeResolver,
349 ExposeRunner,
350 ExposeService,
351 FrameworkRef,
352 LayoutConstraint,
353 LayoutParameter,
354 NameMapping,
355 Offer,
356 OfferConfig,
357 OfferDictionary,
358 OfferDirectory,
359 OfferEventStream,
360 OfferProtocol,
361 OfferResolver,
362 OfferRunner,
363 OfferService,
364 OfferStorage,
365 OnTerminate,
366 ParentRef,
367 Program,
368 Protocol,
369 Ref,
370 ResolvedConfig,
371 ResolvedConfigField,
372 Resolver,
373 ResolverRegistration,
374 Runner,
375 RunnerRegistration,
376 SelfRef,
377 Service,
378 StartupMode,
379 Storage,
380 StorageId,
381 Use,
382 UseConfiguration,
383 UseDictionary,
384 UseDirectory,
385 UseEventStream,
386 UseProtocol,
387 UseRunner,
388 UseService,
389 UseStorage,
390 VoidRef,
391}
392
393impl fmt::Display for DeclType {
394 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
395 let name = match *self {
396 DeclType::AllowedOffers => "AllowedOffers",
407 DeclType::Availability => "Availability",
408 DeclType::Capability => "Capability",
409 DeclType::CapabilityRef => "CapabilityRef",
410 DeclType::Child => "Child",
411 DeclType::ChildRef => "ChildRef",
412 DeclType::Collection => "Collection",
413 DeclType::CollectionRef => "CollectionRef",
414 DeclType::Component => "Component",
415 DeclType::Configuration => "Configuration",
416 DeclType::ConfigChecksum => "ConfigChecksum",
417 DeclType::ConfigField => "ConfigField",
418 DeclType::ConfigMutability => "ConfigMutability",
419 DeclType::ConfigOverride => "ConfigOverride",
420 DeclType::ConfigSchema => "ConfigSchema",
421 DeclType::ConfigSingleValue => "ConfigSingleValue",
422 DeclType::ConfigType => "ConfigType",
423 DeclType::ConfigTypeLayout => "ConfigTypeLayout",
424 DeclType::ConfigValue => "ConfigValue",
425 DeclType::ConfigValuesData => "ConfigValuesData",
426 DeclType::ConfigValueSource => "ConfigValueSource",
427 DeclType::ConfigValueSpec => "ConfigValueSpec",
428 DeclType::ConfigVectorValue => "ConfigVectorValue",
429 DeclType::DebugProtocolRegistration => "DebugProtocolRegistration",
430 DeclType::DebugRef => "DebugRef",
431 DeclType::DebugRegistration => "DebugRegistration",
432 DeclType::DependencyType => "DependencyType",
433 DeclType::Dictionary => "Dictionary",
434 DeclType::Directory => "Directory",
435 DeclType::Durability => "Durability",
436 DeclType::Environment => "Environment",
437 DeclType::EnvironmentExtends => "EnvironmentExtends",
438 DeclType::EventStream => "EventStream",
439 DeclType::EventSubscription => "EventSubscription",
440 DeclType::Expose => "Expose",
441 DeclType::ExposeConfig => "ExposeConfig",
442 DeclType::ExposeDictionary => "ExposeDictionary",
443 DeclType::ExposeDirectory => "ExposeDirectory",
444 DeclType::ExposeProtocol => "ExposeProtocol",
445 DeclType::ExposeResolver => "ExposeResolver",
446 DeclType::ExposeRunner => "ExposeRunner",
447 DeclType::ExposeService => "ExposeService",
448 DeclType::FrameworkRef => "FrameworkRef",
449 DeclType::LayoutConstraint => "LayoutConstraint",
450 DeclType::LayoutParameter => "LayoutParameter",
451 DeclType::NameMapping => "NameMapping",
452 DeclType::Offer => "Offer",
453 DeclType::OfferConfig => "OfferConfig",
454 DeclType::OfferDictionary => "OfferDictionary",
455 DeclType::OfferDirectory => "OfferDirectory",
456 DeclType::OfferEventStream => "OfferEventStream",
457 DeclType::OfferProtocol => "OfferProtocol",
458 DeclType::OfferResolver => "OfferResolver",
459 DeclType::OfferRunner => "OfferRunner",
460 DeclType::OfferService => "OfferService",
461 DeclType::OfferStorage => "OfferStorage",
462 DeclType::OnTerminate => "OnTerminate",
463 DeclType::ParentRef => "ParentRef",
464 DeclType::Program => "Program",
465 DeclType::Protocol => "Protocol",
466 DeclType::Ref => "Ref",
467 DeclType::ResolvedConfig => "ResolvedConfig",
468 DeclType::ResolvedConfigField => "ResolvedConfigField",
469 DeclType::Resolver => "Resolver",
470 DeclType::ResolverRegistration => "ResolverRegistration",
471 DeclType::Runner => "Runner",
472 DeclType::RunnerRegistration => "RunnerRegistration",
473 DeclType::SelfRef => "SelfRef",
474 DeclType::Service => "Service",
475 DeclType::StartupMode => "StartupMode",
476 DeclType::Storage => "Storage",
477 DeclType::StorageId => "StorageId",
478 DeclType::Use => "Use",
479 DeclType::UseConfiguration => "UseConfiguration",
480 DeclType::UseDictionary => "UseDictionary",
481 DeclType::UseDirectory => "UseDirectory",
482 DeclType::UseEventStream => "UseEventStream",
483 DeclType::UseProtocol => "UseProtocol",
484 DeclType::UseRunner => "UseRunner",
485 DeclType::UseService => "UseService",
486 DeclType::UseStorage => "UseStorage",
487 DeclType::VoidRef => "VoidRef",
488 };
489 write!(f, "{}", name)
490 }
491}
492
493#[derive(Debug, PartialEq, Clone)]
494pub struct DeclField {
495 pub decl: DeclType,
496 pub field: String,
497}
498
499impl fmt::Display for DeclField {
500 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
501 write!(f, "{}.{}", &self.decl, &self.field)
502 }
503}
504
505#[derive(Debug, Error, PartialEq, Clone)]
507pub struct ErrorList {
508 pub errs: Vec<Error>,
509}
510
511impl ErrorList {
512 pub(crate) fn new(errs: Vec<Error>) -> ErrorList {
513 ErrorList { errs }
514 }
515}
516
517impl fmt::Display for ErrorList {
518 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
519 let strs: Vec<String> = self.errs.iter().map(|e| format!("{}", e)).collect();
520 write!(f, "{}", strs.join(", "))
521 }
522}
523
524#[cfg(test)]
525mod tests {
526 use super::*;
527
528 #[test]
529 fn test_errors() {
530 assert_eq!(
531 format!("{}", Error::missing_field(DeclType::Child, "keyword")),
532 "Field `keyword` is missing for Child."
533 );
534 assert_eq!(
535 format!("{}", Error::empty_field(DeclType::Child, "keyword")),
536 "Field `keyword` is empty for Child."
537 );
538 assert_eq!(
539 format!("{}", Error::duplicate_field(DeclType::Child, "keyword", "foo")),
540 "\"foo\" is duplicated for field `keyword` in Child."
541 );
542 assert_eq!(
543 format!("{}", Error::invalid_field(DeclType::Child, "keyword")),
544 "Field `keyword` for Child is invalid."
545 );
546 assert_eq!(
547 format!("{}", Error::field_too_long(DeclType::Child, "keyword")),
548 "Field `keyword` for Child is too long."
549 );
550 assert_eq!(
551 format!("{}", Error::field_invalid_segment(DeclType::Child, "keyword")),
552 "Field `keyword` for Child has an invalid path segment."
553 );
554 assert_eq!(
555 format!("{}", Error::invalid_child(DeclType::Child, "source", "child")),
556 "\"child\" is referenced in Child.source but it does not appear in children."
557 );
558 assert_eq!(
559 format!("{}", Error::invalid_collection(DeclType::Child, "source", "child")),
560 "\"child\" is referenced in Child.source but it does not appear in collections."
561 );
562 }
563}