cml/types/capability.rs
1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::{
6 AnyRef, AsClause, Canonicalize, CapabilityClause, ConfigNestedValueType, ConfigType,
7 FilterClause, PathClause, SpannedCapabilityClause, always_one, option_one_or_many_as_ref,
8};
9
10use crate::one_or_many::OneOrMany;
11use crate::types::right::{Rights, RightsClause};
12pub use cm_types::{
13 Availability, BorrowedName, BoundedName, DeliveryType, DependencyType, HandleType, Name,
14 OnTerminate, ParseError, Path, RelativePath, StartupMode, StorageId, Url,
15};
16use cml_macro::Reference;
17use json_spanned_value::Spanned;
18use reference_doc::ReferenceDoc;
19use serde::{Deserialize, Serialize};
20use serde_json::{Map, Value};
21use std::num::NonZeroU32;
22
23use std::fmt;
24
25#[derive(Deserialize, Debug, PartialEq, Clone, ReferenceDoc, Serialize, Default)]
26#[serde(deny_unknown_fields)]
27#[reference_doc(fields_as = "list")]
28pub struct Capability {
29 /// The [name](#name) for this service capability. Specifying `path` is valid
30 /// only when this value is a string.
31 #[serde(skip_serializing_if = "Option::is_none")]
32 #[reference_doc(skip = true)]
33 pub service: Option<OneOrMany<Name>>,
34
35 /// The [name](#name) for this protocol capability. Specifying `path` is valid
36 /// only when this value is a string.
37 #[serde(skip_serializing_if = "Option::is_none")]
38 #[reference_doc(skip = true)]
39 pub protocol: Option<OneOrMany<Name>>,
40
41 /// The [name](#name) for this directory capability.
42 #[serde(skip_serializing_if = "Option::is_none")]
43 #[reference_doc(skip = true)]
44 pub directory: Option<Name>,
45
46 /// The [name](#name) for this storage capability.
47 #[serde(skip_serializing_if = "Option::is_none")]
48 #[reference_doc(skip = true)]
49 pub storage: Option<Name>,
50
51 /// The [name](#name) for this runner capability.
52 #[serde(skip_serializing_if = "Option::is_none")]
53 #[reference_doc(skip = true)]
54 pub runner: Option<Name>,
55
56 /// The [name](#name) for this resolver capability.
57 #[serde(skip_serializing_if = "Option::is_none")]
58 #[reference_doc(skip = true)]
59 pub resolver: Option<Name>,
60
61 /// The [name](#name) for this event_stream capability.
62 #[serde(skip_serializing_if = "Option::is_none")]
63 #[reference_doc(skip = true)]
64 pub event_stream: Option<OneOrMany<Name>>,
65
66 /// The [name](#name) for this dictionary capability.
67 #[serde(skip_serializing_if = "Option::is_none")]
68 #[reference_doc(skip = true)]
69 pub dictionary: Option<Name>,
70
71 /// The [name](#name) for this configuration capability.
72 #[serde(skip_serializing_if = "Option::is_none")]
73 #[reference_doc(skip = true)]
74 pub config: Option<Name>,
75
76 /// The path within the [outgoing directory][glossary.outgoing directory] of the component's
77 /// program to source the capability.
78 ///
79 /// For `protocol` and `service`, defaults to `/svc/${protocol}`, otherwise required.
80 ///
81 /// For `protocol`, the target of the path MUST be a channel, which tends to speak
82 /// the protocol matching the name of this capability.
83 ///
84 /// For `service`, `directory`, the target of the path MUST be a directory.
85 ///
86 /// For `runner`, the target of the path MUST be a channel and MUST speak
87 /// the protocol `fuchsia.component.runner.ComponentRunner`.
88 ///
89 /// For `resolver`, the target of the path MUST be a channel and MUST speak
90 /// the protocol `fuchsia.component.resolution.Resolver`.
91 ///
92 /// For `dictionary`, this is optional. If provided, it is a path to a
93 /// `fuchsia.component.sandbox/DictionaryRouter` served by the program which should return a
94 /// `fuchsia.component.sandbox/DictionaryRef`, by which the program may dynamically provide
95 /// a dictionary from itself. If this is set for `dictionary`, `offer` to this dictionary
96 /// is not allowed.
97 #[serde(skip_serializing_if = "Option::is_none")]
98 pub path: Option<Path>,
99
100 /// (`directory` only) The maximum [directory rights][doc-directory-rights] that may be set
101 /// when using this directory.
102 #[serde(skip_serializing_if = "Option::is_none")]
103 #[reference_doc(json_type = "array of string")]
104 pub rights: Option<Rights>,
105
106 /// (`storage` only) The source component of an existing directory capability backing this
107 /// storage capability, one of:
108 /// - `parent`: The component's parent.
109 /// - `self`: This component.
110 /// - `#<child-name>`: A [reference](#references) to a child component
111 /// instance.
112 #[serde(skip_serializing_if = "Option::is_none")]
113 pub from: Option<CapabilityFromRef>,
114
115 /// (`storage` only) The [name](#name) of the directory capability backing the storage. The
116 /// capability must be available from the component referenced in `from`.
117 #[serde(skip_serializing_if = "Option::is_none")]
118 pub backing_dir: Option<Name>,
119
120 /// (`storage` only) A subdirectory within `backing_dir` where per-component isolated storage
121 /// directories are created
122 #[serde(skip_serializing_if = "Option::is_none")]
123 pub subdir: Option<RelativePath>,
124
125 /// (`storage` only) The identifier used to isolated storage for a component, one of:
126 /// - `static_instance_id`: The instance ID in the component ID index is used
127 /// as the key for a component's storage. Components which are not listed in
128 /// the component ID index will not be able to use this storage capability.
129 /// - `static_instance_id_or_moniker`: If the component is listed in the
130 /// component ID index, the instance ID is used as the key for a component's
131 /// storage. Otherwise, the component's moniker from the storage
132 /// capability is used.
133 #[serde(skip_serializing_if = "Option::is_none")]
134 pub storage_id: Option<StorageId>,
135
136 /// (`configuration` only) The type of configuration, one of:
137 /// - `bool`: Boolean type.
138 /// - `uint8`: Unsigned 8 bit type.
139 /// - `uint16`: Unsigned 16 bit type.
140 /// - `uint32`: Unsigned 32 bit type.
141 /// - `uint64`: Unsigned 64 bit type.
142 /// - `int8`: Signed 8 bit type.
143 /// - `int16`: Signed 16 bit type.
144 /// - `int32`: Signed 32 bit type.
145 /// - `int64`: Signed 64 bit type.
146 /// - `string`: ASCII string type.
147 /// - `vector`: Vector type. See `element` for the type of the element within the vector.
148 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
149 #[reference_doc(rename = "type")]
150 pub config_type: Option<ConfigType>,
151
152 /// (`configuration` only) Only supported if this configuration `type` is 'string'.
153 /// This is the max size of the string.
154 #[serde(rename = "max_size", skip_serializing_if = "Option::is_none")]
155 #[reference_doc(rename = "max_size")]
156 pub config_max_size: Option<NonZeroU32>,
157
158 /// (`configuration` only) Only supported if this configuration `type` is 'vector'.
159 /// This is the max number of elements in the vector.
160 #[serde(rename = "max_count", skip_serializing_if = "Option::is_none")]
161 #[reference_doc(rename = "max_count")]
162 pub config_max_count: Option<NonZeroU32>,
163
164 /// (`configuration` only) Only supported if this configuration `type` is 'vector'.
165 /// This is the type of the elements in the configuration vector.
166 ///
167 /// Example (simple type):
168 ///
169 /// ```json5
170 /// { type: "uint8" }
171 /// ```
172 ///
173 /// Example (string type):
174 ///
175 /// ```json5
176 /// {
177 /// type: "string",
178 /// max_size: 100,
179 /// }
180 /// ```
181 #[serde(rename = "element", skip_serializing_if = "Option::is_none")]
182 #[reference_doc(rename = "element", json_type = "object")]
183 pub config_element_type: Option<ConfigNestedValueType>,
184
185 /// (`configuration` only) The value of the configuration.
186 #[serde(skip_serializing_if = "Option::is_none")]
187 pub value: Option<serde_json::Value>,
188
189 /// (`protocol` only) Specifies when the framework will open the protocol
190 /// from this component's outgoing directory when someone requests the
191 /// capability. Allowed values are:
192 ///
193 /// - `eager`: (default) the framework will open the capability as soon as
194 /// some consumer component requests it.
195 /// - `on_readable`: the framework will open the capability when the server
196 /// endpoint pipelined in a connection request becomes readable.
197 ///
198 #[serde(skip_serializing_if = "Option::is_none")]
199 pub delivery: Option<DeliveryType>,
200}
201
202impl Canonicalize for Capability {
203 fn canonicalize(&mut self) {
204 // Sort the names of the capabilities. Only capabilities with OneOrMany values are included here.
205 if let Some(service) = &mut self.service {
206 service.canonicalize()
207 } else if let Some(protocol) = &mut self.protocol {
208 protocol.canonicalize()
209 } else if let Some(event_stream) = &mut self.event_stream {
210 event_stream.canonicalize()
211 }
212 }
213}
214
215impl AsClause for Capability {
216 fn r#as(&self) -> Option<&BorrowedName> {
217 None
218 }
219}
220
221impl PathClause for Capability {
222 fn path(&self) -> Option<&Path> {
223 self.path.as_ref()
224 }
225}
226
227impl FilterClause for Capability {
228 fn filter(&self) -> Option<&Map<String, Value>> {
229 None
230 }
231}
232
233impl RightsClause for Capability {
234 fn rights(&self) -> Option<&Rights> {
235 self.rights.as_ref()
236 }
237}
238
239impl CapabilityClause for Capability {
240 fn service(&self) -> Option<OneOrMany<&BorrowedName>> {
241 option_one_or_many_as_ref(&self.service)
242 }
243 fn protocol(&self) -> Option<OneOrMany<&BorrowedName>> {
244 option_one_or_many_as_ref(&self.protocol)
245 }
246 fn directory(&self) -> Option<OneOrMany<&BorrowedName>> {
247 self.directory.as_ref().map(|n| OneOrMany::One(n.as_ref()))
248 }
249 fn storage(&self) -> Option<OneOrMany<&BorrowedName>> {
250 self.storage.as_ref().map(|n| OneOrMany::One(n.as_ref()))
251 }
252 fn runner(&self) -> Option<OneOrMany<&BorrowedName>> {
253 self.runner.as_ref().map(|n| OneOrMany::One(n.as_ref()))
254 }
255 fn resolver(&self) -> Option<OneOrMany<&BorrowedName>> {
256 self.resolver.as_ref().map(|n| OneOrMany::One(n.as_ref()))
257 }
258 fn event_stream(&self) -> Option<OneOrMany<&BorrowedName>> {
259 option_one_or_many_as_ref(&self.event_stream)
260 }
261 fn dictionary(&self) -> Option<OneOrMany<&BorrowedName>> {
262 self.dictionary.as_ref().map(|n| OneOrMany::One(n.as_ref()))
263 }
264 fn config(&self) -> Option<OneOrMany<&BorrowedName>> {
265 self.config.as_ref().map(|n| OneOrMany::One(n.as_ref()))
266 }
267
268 fn set_service(&mut self, o: Option<OneOrMany<Name>>) {
269 self.service = o;
270 }
271 fn set_protocol(&mut self, o: Option<OneOrMany<Name>>) {
272 self.protocol = o;
273 }
274 fn set_directory(&mut self, o: Option<OneOrMany<Name>>) {
275 self.directory = always_one(o);
276 }
277 fn set_storage(&mut self, o: Option<OneOrMany<Name>>) {
278 self.storage = always_one(o);
279 }
280 fn set_runner(&mut self, o: Option<OneOrMany<Name>>) {
281 self.runner = always_one(o);
282 }
283 fn set_resolver(&mut self, o: Option<OneOrMany<Name>>) {
284 self.resolver = always_one(o);
285 }
286 fn set_event_stream(&mut self, o: Option<OneOrMany<Name>>) {
287 self.event_stream = o;
288 }
289 fn set_dictionary(&mut self, o: Option<OneOrMany<Name>>) {
290 self.dictionary = always_one(o);
291 }
292
293 fn set_config(&mut self, o: Option<OneOrMany<Name>>) {
294 self.config = always_one(o);
295 }
296
297 fn availability(&self) -> Option<Availability> {
298 None
299 }
300 fn set_availability(&mut self, _a: Option<Availability>) {}
301
302 fn decl_type(&self) -> &'static str {
303 "capability"
304 }
305 fn supported(&self) -> &[&'static str] {
306 &[
307 "service",
308 "protocol",
309 "directory",
310 "storage",
311 "runner",
312 "resolver",
313 "event_stream",
314 "dictionary",
315 "config",
316 ]
317 }
318 fn are_many_names_allowed(&self) -> bool {
319 ["service", "protocol", "event_stream"].contains(&self.capability_type().unwrap())
320 }
321}
322
323#[derive(Deserialize, Debug, PartialEq, Clone, ReferenceDoc, Default)]
324#[serde(deny_unknown_fields)]
325pub struct SpannedCapability {
326 /// The [name](#name) for this service capability. Specifying `path` is valid
327 /// only when this value is a string.
328 pub service: Option<OneOrMany<Name>>,
329
330 /// The [name](#name) for this protocol capability. Specifying `path` is valid
331 /// only when this value is a string.
332 pub protocol: Option<OneOrMany<Name>>,
333
334 /// The [name](#name) for this directory capability.
335 pub directory: Option<Spanned<Name>>,
336
337 /// The [name](#name) for this storage capability.
338 pub storage: Option<Spanned<Name>>,
339
340 /// The [name](#name) for this runner capability.
341 pub runner: Option<Spanned<Name>>,
342
343 /// The [name](#name) for this resolver capability.
344 pub resolver: Option<Spanned<Name>>,
345
346 /// The [name](#name) for this event_stream capability.
347 pub event_stream: Option<OneOrMany<Name>>,
348
349 /// The [name](#name) for this dictionary capability.
350 pub dictionary: Option<Name>,
351
352 /// The [name](#name) for this configuration capability.
353 pub config: Option<Name>,
354
355 /// The path within the [outgoing directory][glossary.outgoing directory] of the component's
356 /// program to source the capability.
357 ///
358 /// For `protocol` and `service`, defaults to `/svc/${protocol}`, otherwise required.
359 ///
360 /// For `protocol`, the target of the path MUST be a channel, which tends to speak
361 /// the protocol matching the name of this capability.
362 ///
363 /// For `service`, `directory`, the target of the path MUST be a directory.
364 ///
365 /// For `runner`, the target of the path MUST be a channel and MUST speak
366 /// the protocol `fuchsia.component.runner.ComponentRunner`.
367 ///
368 /// For `resolver`, the target of the path MUST be a channel and MUST speak
369 /// the protocol `fuchsia.component.resolution.Resolver`.
370 ///
371 /// For `dictionary`, this is optional. If provided, it is a path to a
372 /// `fuchsia.component.sandbox/DictionaryRouter` served by the program which should return a
373 /// `fuchsia.component.sandbox/DictionaryRef`, by which the program may dynamically provide
374 /// a dictionary from itself. If this is set for `dictionary`, `offer` to this dictionary
375 /// is not allowed.
376 pub path: Option<Spanned<Path>>,
377
378 /// (`directory` only) The maximum [directory rights][doc-directory-rights] that may be set
379 /// when using this directory.
380 pub rights: Option<Rights>,
381
382 /// (`storage` only) The source component of an existing directory capability backing this
383 /// storage capability, one of:
384 /// - `parent`: The component's parent.
385 /// - `self`: This component.
386 /// - `#<child-name>`: A [reference](#references) to a child component
387 /// instance.
388 pub from: Option<CapabilityFromRef>,
389
390 /// (`storage` only) The [name](#name) of the directory capability backing the storage. The
391 /// capability must be available from the component referenced in `from`.
392 pub backing_dir: Option<Name>,
393
394 /// (`storage` only) A subdirectory within `backing_dir` where per-component isolated storage
395 /// directories are created
396 pub subdir: Option<RelativePath>,
397
398 /// (`storage` only) The identifier used to isolated storage for a component, one of:
399 /// - `static_instance_id`: The instance ID in the component ID index is used
400 /// as the key for a component's storage. Components which are not listed in
401 /// the component ID index will not be able to use this storage capability.
402 /// - `static_instance_id_or_moniker`: If the component is listed in the
403 /// component ID index, the instance ID is used as the key for a component's
404 /// storage. Otherwise, the component's moniker from the storage
405 /// capability is used.
406 pub storage_id: Option<StorageId>,
407
408 /// (`configuration` only) The type of configuration, one of:
409 /// - `bool`: Boolean type.
410 /// - `uint8`: Unsigned 8 bit type.
411 /// - `uint16`: Unsigned 16 bit type.
412 /// - `uint32`: Unsigned 32 bit type.
413 /// - `uint64`: Unsigned 64 bit type.
414 /// - `int8`: Signed 8 bit type.
415 /// - `int16`: Signed 16 bit type.
416 /// - `int32`: Signed 32 bit type.
417 /// - `int64`: Signed 64 bit type.
418 /// - `string`: ASCII string type.
419 /// - `vector`: Vector type. See `element` for the type of the element within the vector.
420 pub config_type: Option<ConfigType>,
421
422 /// (`configuration` only) Only supported if this configuration `type` is 'string'.
423 /// This is the max size of the string.
424 pub config_max_size: Option<NonZeroU32>,
425
426 /// (`configuration` only) Only supported if this configuration `type` is 'vector'.
427 /// This is the max number of elements in the vector.
428 pub config_max_count: Option<NonZeroU32>,
429
430 /// (`configuration` only) Only supported if this configuration `type` is 'vector'.
431 /// This is the type of the elements in the configuration vector.
432 ///
433 /// Example (simple type):
434 ///
435 /// ```json5
436 /// { type: "uint8" }
437 /// ```
438 ///
439 /// Example (string type):
440 ///
441 /// ```json5
442 /// {
443 /// type: "string",
444 /// max_size: 100,
445 /// }
446 /// ```
447 pub config_element_type: Option<ConfigNestedValueType>,
448
449 /// (`configuration` only) The value of the configuration.
450 pub value: Option<serde_json::Value>,
451
452 /// (`protocol` only) Specifies when the framework will open the protocol
453 /// from this component's outgoing directory when someone requests the
454 /// capability. Allowed values are:
455 ///
456 /// - `eager`: (default) the framework will open the capability as soon as
457 /// some consumer component requests it.
458 /// - `on_readable`: the framework will open the capability when the server
459 /// endpoint pipelined in a connection request becomes readable.
460 ///
461 pub delivery: Option<DeliveryType>,
462}
463
464impl SpannedCapabilityClause for SpannedCapability {
465 fn service(&self) -> Option<OneOrMany<&BorrowedName>> {
466 option_one_or_many_as_ref(&self.service)
467 }
468 fn protocol(&self) -> Option<OneOrMany<&BorrowedName>> {
469 option_one_or_many_as_ref(&self.protocol)
470 }
471 fn directory(&self) -> Option<OneOrMany<&BorrowedName>> {
472 self.directory.as_ref().map(|n| OneOrMany::One((&**n).as_ref()))
473 }
474 fn storage(&self) -> Option<OneOrMany<&BorrowedName>> {
475 self.storage.as_ref().map(|n| OneOrMany::One((&**n).as_ref()))
476 }
477 fn runner(&self) -> Option<OneOrMany<&BorrowedName>> {
478 self.runner.as_ref().map(|n| OneOrMany::One((&**n).as_ref()))
479 }
480 fn resolver(&self) -> Option<OneOrMany<&BorrowedName>> {
481 self.resolver.as_ref().map(|n| OneOrMany::One((&**n).as_ref()))
482 }
483 fn event_stream(&self) -> Option<OneOrMany<&BorrowedName>> {
484 option_one_or_many_as_ref(&self.event_stream)
485 }
486 fn dictionary(&self) -> Option<OneOrMany<&BorrowedName>> {
487 self.dictionary.as_ref().map(|n| OneOrMany::One(n.as_ref()))
488 }
489 fn config(&self) -> Option<OneOrMany<&BorrowedName>> {
490 self.config.as_ref().map(|n| OneOrMany::One(n.as_ref()))
491 }
492
493 fn decl_type(&self) -> &'static str {
494 "capability"
495 }
496 fn supported(&self) -> &[&'static str] {
497 &[
498 "service",
499 "protocol",
500 "directory",
501 "storage",
502 "runner",
503 "resolver",
504 "event_stream",
505 "dictionary",
506 "config",
507 ]
508 }
509}
510
511/// A reference in a `storage from`.
512#[derive(Debug, PartialEq, Eq, Hash, Clone, Reference)]
513#[reference(expected = "\"parent\", \"self\", or \"#<child-name>\"")]
514pub enum CapabilityFromRef {
515 /// A reference to a child.
516 Named(Name),
517 /// A reference to the parent.
518 Parent,
519 /// A reference to this component.
520 Self_,
521}