routing/bedrock/
request_metadata.rs

1// Copyright 2024 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::rights::Rights;
6use cm_rust::Availability;
7use sandbox::{Capability, Data, Dict, DictKey};
8use {fidl_fuchsia_component_sandbox as fsandbox, fidl_fuchsia_io as fio};
9
10/// A route request metadata key for the capability type.
11pub const METADATA_KEY_TYPE: &'static str = "type";
12
13/// The capability type value for a protocol.
14pub const TYPE_PROTOCOL: &'static str = "protocol";
15pub const TYPE_DICTIONARY: &'static str = "dictionary";
16pub const TYPE_CONFIG: &'static str = "configuration";
17
18/// A type which has accessors for route request metadata of type T.
19pub trait Metadata<T> {
20    /// A key string used for setting and getting the metadata.
21    const KEY: &'static str;
22
23    /// Infallibly assigns `value` to `self`.
24    fn set_metadata(&self, value: T);
25
26    /// Retrieves the subdir metadata from `self`, if present.
27    fn get_metadata(&self) -> Option<T>;
28}
29
30impl Metadata<Availability> for Dict {
31    const KEY: &'static str = "availability";
32
33    fn set_metadata(&self, value: Availability) {
34        let key = DictKey::new(<Self as Metadata<Availability>>::KEY)
35            .expect("dict key creation failed unexpectedly");
36        match self.insert(key, Capability::Data(Data::String(value.to_string()))) {
37            // When an entry already exists for a key in a Dict, insert() will
38            // still replace that entry with the new value, even though it
39            // returns an ItemAlreadyExists error. As a result, we can treat
40            // ItemAlreadyExists as a success case.
41            Ok(()) | Err(fsandbox::CapabilityStoreError::ItemAlreadyExists) => (),
42            // Dict::insert() only returns `CapabilityStoreError::ItemAlreadyExists` variant
43            Err(e) => panic!("unexpected error variant returned from Dict::insert(): {e:?}"),
44        }
45    }
46
47    fn get_metadata(&self) -> Option<Availability> {
48        let key = DictKey::new(<Self as Metadata<Availability>>::KEY)
49            .expect("dict key creation failed unexpectedly");
50        let capability = self.get(&key).ok()??;
51        match capability {
52            Capability::Data(Data::String(availability)) => match availability.as_str() {
53                "Optional" => Some(Availability::Optional),
54                "Required" => Some(Availability::Required),
55                "SameAsTarget" => Some(Availability::SameAsTarget),
56                "Transitional" => Some(Availability::Transitional),
57                _ => None,
58            },
59            _ => None,
60        }
61    }
62}
63
64impl Metadata<Rights> for Dict {
65    const KEY: &'static str = "rights";
66
67    fn set_metadata(&self, value: Rights) {
68        let key = DictKey::new(<Self as Metadata<Rights>>::KEY)
69            .expect("dict key creation failed unexpectedly");
70        match self.insert(key, Capability::Data(Data::Uint64(value.into()))) {
71            // When an entry already exists for a key in a Dict, insert() will
72            // still replace that entry with the new value, even though it
73            // returns an ItemAlreadyExists error. As a result, we can treat
74            // ItemAlreadyExists as a success case.
75            Ok(()) | Err(fsandbox::CapabilityStoreError::ItemAlreadyExists) => (),
76            // Dict::insert() only returns `CapabilityStoreError::ItemAlreadyExists` variant
77            Err(e) => panic!("unexpected error variant returned from Dict::insert(): {e:?}"),
78        }
79    }
80
81    fn get_metadata(&self) -> Option<Rights> {
82        let key = DictKey::new(<Self as Metadata<Rights>>::KEY)
83            .expect("dict key creation failed unexpectedly");
84        let capability = self.get(&key).ok()??;
85        let rights = match capability {
86            Capability::Data(Data::Uint64(rights)) => fio::Operations::from_bits(rights)?,
87            _ => None?,
88        };
89        Some(Rights::from(rights))
90    }
91}
92
93/// Returns a `Dict` containing Router Request metadata specifying a Protocol porcelain type.
94pub fn protocol_metadata(availability: cm_types::Availability) -> sandbox::Dict {
95    let metadata = sandbox::Dict::new();
96    metadata
97        .insert(
98            cm_types::Name::new(METADATA_KEY_TYPE).unwrap(),
99            sandbox::Capability::Data(sandbox::Data::String(String::from(TYPE_PROTOCOL))),
100        )
101        .unwrap();
102    metadata.set_metadata(availability);
103    metadata
104}
105
106/// Returns a `Dict` containing Router Request metadata specifying a Dictionary porcelain type.
107pub fn dictionary_metadata(availability: cm_types::Availability) -> sandbox::Dict {
108    let metadata = sandbox::Dict::new();
109    metadata
110        .insert(
111            cm_types::Name::new(METADATA_KEY_TYPE).unwrap(),
112            sandbox::Capability::Data(sandbox::Data::String(String::from(TYPE_DICTIONARY))),
113        )
114        .unwrap();
115    metadata.set_metadata(availability);
116    metadata
117}
118
119/// Returns a `Dict` containing Router Request metadata specifying a Config porcelain type.
120pub fn config_metadata(availability: cm_types::Availability) -> sandbox::Dict {
121    let metadata = sandbox::Dict::new();
122    metadata
123        .insert(
124            cm_types::Name::new(METADATA_KEY_TYPE).unwrap(),
125            sandbox::Capability::Data(sandbox::Data::String(String::from(TYPE_CONFIG))),
126        )
127        .unwrap();
128    metadata.set_metadata(availability);
129    metadata
130}
131
132/// Returns a `Dict` containing Router Request metadata specifying a Runner porcelain type.
133pub fn runner_metadata(availability: cm_types::Availability) -> sandbox::Dict {
134    let metadata = sandbox::Dict::new();
135    metadata
136        .insert(
137            cm_types::Name::new(METADATA_KEY_TYPE).unwrap(),
138            sandbox::Capability::Data(sandbox::Data::String(
139                cm_rust::CapabilityTypeName::Runner.to_string(),
140            )),
141        )
142        .unwrap();
143    metadata.set_metadata(availability);
144    metadata
145}
146
147/// Returns a `Dict` Containing Router Request metadata specifying a Resolver porcelain type.
148pub fn resolver_metadata(availability: cm_types::Availability) -> sandbox::Dict {
149    let metadata = sandbox::Dict::new();
150    metadata
151        .insert(
152            cm_types::Name::new(METADATA_KEY_TYPE).unwrap(),
153            sandbox::Capability::Data(sandbox::Data::String(
154                cm_rust::CapabilityTypeName::Resolver.to_string(),
155            )),
156        )
157        .unwrap();
158    metadata.set_metadata(availability);
159    metadata
160}
161
162/// Returns a `Dict` Containing Router Request metadata specifying a Service porcelain type.
163pub fn service_metadata(availability: cm_types::Availability) -> sandbox::Dict {
164    let metadata = sandbox::Dict::new();
165    metadata
166        .insert(
167            cm_types::Name::new(METADATA_KEY_TYPE).unwrap(),
168            sandbox::Capability::Data(sandbox::Data::String(
169                cm_rust::CapabilityTypeName::Service.to_string(),
170            )),
171        )
172        .unwrap();
173    metadata.set_metadata(availability);
174    metadata
175}