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