1use crate::DictExt;
6use crate::bedrock::with_policy_check::WithPolicyCheck;
7use crate::capability_source::{CapabilitySource, ComponentCapability, ComponentSource};
8use crate::component_instance::{ComponentInstanceInterface, WeakComponentInstanceInterface};
9use async_trait::async_trait;
10use cm_rust::NativeIntoFidl;
11use cm_types::Path;
12use log::warn;
13use router_error::RouterError;
14use sandbox::{
15 Connector, Data, Dict, DirConnector, Request, Routable, Router, RouterResponse,
16 WeakInstanceToken,
17};
18use std::sync::Arc;
19
20pub trait ProgramOutputGenerator<C: ComponentInstanceInterface + 'static> {
21 fn new_program_dictionary_router(
24 &self,
25 component: WeakComponentInstanceInterface<C>,
26 path: Path,
27 capability: ComponentCapability,
28 ) -> Router<Dict>;
29
30 fn new_outgoing_dir_connector_router(
33 &self,
34 component: &Arc<C>,
35 decl: &cm_rust::ComponentDecl,
36 capability: &cm_rust::CapabilityDecl,
37 ) -> Router<Connector>;
38
39 fn new_outgoing_dir_dir_connector_router(
42 &self,
43 component: &Arc<C>,
44 decl: &cm_rust::ComponentDecl,
45 capability: &cm_rust::CapabilityDecl,
46 ) -> Router<DirConnector>;
47}
48
49pub fn build_program_output_dictionary<C: ComponentInstanceInterface + 'static>(
50 component: &Arc<C>,
51 decl: &cm_rust::ComponentDecl,
52 router_gen: &impl ProgramOutputGenerator<C>,
53) -> (Dict, Dict) {
54 let program_output_dict = Dict::new();
55 let declared_dictionaries = Dict::new();
56 for capability in &decl.capabilities {
57 extend_dict_with_capability(
58 component,
59 decl,
60 capability,
61 &program_output_dict,
62 &declared_dictionaries,
63 router_gen,
64 );
65 }
66 (program_output_dict, declared_dictionaries)
67}
68
69fn extend_dict_with_capability<C: ComponentInstanceInterface + 'static>(
72 component: &Arc<C>,
73 decl: &cm_rust::ComponentDecl,
74 capability: &cm_rust::CapabilityDecl,
75 program_output_dict: &Dict,
76 declared_dictionaries: &Dict,
77 router_gen: &impl ProgramOutputGenerator<C>,
78) {
79 match capability {
80 cm_rust::CapabilityDecl::Service(_) => {
81 let router =
82 router_gen.new_outgoing_dir_dir_connector_router(component, decl, capability);
83 let router = router.with_policy_check::<C>(
84 CapabilitySource::Component(ComponentSource {
85 capability: ComponentCapability::from(capability.clone()),
86 moniker: component.moniker().clone(),
87 }),
88 component.policy_checker().clone(),
89 );
90 match program_output_dict.insert_capability(capability.name(), router.into()) {
91 Ok(()) => (),
92 Err(e) => {
93 warn!("failed to add {} to program output dict: {e:?}", capability.name())
94 }
95 }
96 }
97 cm_rust::CapabilityDecl::Directory(_) => {
98 let router =
99 router_gen.new_outgoing_dir_dir_connector_router(component, decl, capability);
100 let router = router.with_policy_check::<C>(
101 CapabilitySource::Component(ComponentSource {
102 capability: ComponentCapability::from(capability.clone()),
103 moniker: component.moniker().clone(),
104 }),
105 component.policy_checker().clone(),
106 );
107 match program_output_dict.insert_capability(capability.name(), router.into()) {
108 Ok(()) => (),
109 Err(e) => {
110 warn!("failed to add {} to program output dict: {e:?}", capability.name())
111 }
112 }
113 }
114 cm_rust::CapabilityDecl::Protocol(_)
115 | cm_rust::CapabilityDecl::Runner(_)
116 | cm_rust::CapabilityDecl::Resolver(_) => {
117 let router = router_gen.new_outgoing_dir_connector_router(component, decl, capability);
118 let router = router.with_policy_check::<C>(
119 CapabilitySource::Component(ComponentSource {
120 capability: ComponentCapability::from(capability.clone()),
121 moniker: component.moniker().clone(),
122 }),
123 component.policy_checker().clone(),
124 );
125 match program_output_dict.insert_capability(capability.name(), router.into()) {
126 Ok(()) => (),
127 Err(e) => {
128 warn!("failed to add {} to program output dict: {e:?}", capability.name())
129 }
130 }
131 }
132 cm_rust::CapabilityDecl::Dictionary(d) => {
133 extend_dict_with_dictionary(
134 component,
135 d,
136 program_output_dict,
137 declared_dictionaries,
138 router_gen,
139 );
140 }
141 cm_rust::CapabilityDecl::Config(c) => {
142 let data = sandbox::Data::Bytes(
143 fidl::persist(&c.value.clone().native_into_fidl()).unwrap().into(),
144 );
145 struct ConfigRouter {
146 data: Data,
147 source: CapabilitySource,
148 }
149 #[async_trait]
150 impl Routable<Data> for ConfigRouter {
151 async fn route(
152 &self,
153 _request: Option<Request>,
154 debug: bool,
155 _target: WeakInstanceToken,
156 ) -> Result<RouterResponse<Data>, RouterError> {
157 if debug {
158 Ok(RouterResponse::Debug(
159 self.source
160 .clone()
161 .try_into()
162 .expect("failed to convert capability source to dictionary"),
163 ))
164 } else {
165 Ok(RouterResponse::Capability(self.data.clone()))
166 }
167 }
168 }
169 let source = CapabilitySource::Component(ComponentSource {
170 capability: ComponentCapability::from(capability.clone()),
171 moniker: component.moniker().clone(),
172 });
173 let router = Router::new(ConfigRouter { data, source: source.clone() });
174 let router = router.with_policy_check::<C>(source, component.policy_checker().clone());
175 match program_output_dict.insert_capability(capability.name(), router.into()) {
176 Ok(()) => (),
177 Err(e) => {
178 warn!("failed to add {} to program output dict: {e:?}", capability.name())
179 }
180 }
181 }
182 cm_rust::CapabilityDecl::EventStream(_) | cm_rust::CapabilityDecl::Storage(_) => {
183 return;
185 }
186 }
187}
188
189fn extend_dict_with_dictionary<C: ComponentInstanceInterface + 'static>(
190 component: &Arc<C>,
191 decl: &cm_rust::DictionaryDecl,
192 program_output_dict: &Dict,
193 declared_dictionaries: &Dict,
194 router_gen: &impl ProgramOutputGenerator<C>,
195) {
196 let router;
197 let declared_dict;
198 if let Some(source_path) = decl.source_path.as_ref() {
199 router = router_gen.new_program_dictionary_router(
201 component.as_weak(),
202 source_path.clone(),
203 ComponentCapability::Dictionary(decl.clone()),
204 );
205 declared_dict = None;
206 } else {
207 let dict = Dict::new();
208 router = make_simple_dict_router(dict.clone(), component, decl);
209 declared_dict = Some(dict);
210 }
211 if let Some(dict) = declared_dict {
212 match declared_dictionaries.insert_capability(&decl.name, dict.into()) {
213 Ok(()) => (),
214 Err(e) => warn!("failed to add {} to declared dicts: {e:?}", decl.name),
215 };
216 }
217 match program_output_dict.insert_capability(&decl.name, router.into()) {
218 Ok(()) => (),
219 Err(e) => warn!("failed to add {} to program output dict: {e:?}", decl.name),
220 }
221}
222
223fn make_simple_dict_router<C: ComponentInstanceInterface + 'static>(
225 dict: Dict,
226 component: &Arc<C>,
227 decl: &cm_rust::DictionaryDecl,
228) -> Router<Dict> {
229 struct DictRouter {
230 dict: Dict,
231 source: CapabilitySource,
232 }
233 #[async_trait]
234 impl Routable<Dict> for DictRouter {
235 async fn route(
236 &self,
237 _request: Option<Request>,
238 debug: bool,
239 _target: WeakInstanceToken,
240 ) -> Result<RouterResponse<Dict>, RouterError> {
241 if debug {
242 Ok(RouterResponse::Debug(
243 self.source
244 .clone()
245 .try_into()
246 .expect("failed to convert capability source to dictionary"),
247 ))
248 } else {
249 Ok(RouterResponse::Capability(self.dict.clone().into()))
250 }
251 }
252 }
253 let source = CapabilitySource::Component(ComponentSource {
254 capability: ComponentCapability::Dictionary(decl.clone()),
255 moniker: component.moniker().clone(),
256 });
257 Router::<Dict>::new(DictRouter { dict, source })
258}