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