1use crate::bedrock::dict_ext::DictExt;
6use crate::bedrock::request_metadata;
7use crate::capability_source::{
8 CapabilitySource, CapabilityToCapabilitySource, ComponentCapability, ComponentSource,
9};
10use crate::component_instance::ComponentInstanceInterface;
11use crate::{RouteRequest, RouterResponse, RoutingError};
12use cm_rust::FidlIntoNative;
13use sandbox::Data;
14use std::sync::Arc;
15
16pub fn get_use_config_from_key<'a>(
19 key: &str,
20 decl: &'a cm_rust::ComponentDecl,
21) -> Option<&'a cm_rust::UseConfigurationDecl> {
22 decl.uses.iter().find_map(|use_| match use_ {
23 cm_rust::UseDecl::Config(c) => (c.target_name == key).then_some(c),
24 _ => None,
25 })
26}
27
28fn source_to_value(
29 default: &Option<cm_rust::ConfigValue>,
30 source: CapabilitySource,
31) -> Result<Option<cm_rust::ConfigValue>, RoutingError> {
32 let moniker = source.source_moniker();
33 let cap = match source {
34 CapabilitySource::Void(_) => {
35 return Ok(default.clone());
36 }
37 CapabilitySource::Capability(CapabilityToCapabilitySource {
38 source_capability, ..
39 }) => source_capability,
40 CapabilitySource::Component(ComponentSource { capability, .. }) => capability,
41 o => {
42 return Err(RoutingError::unsupported_route_source(moniker, o.type_name().to_string()));
43 }
44 };
45
46 let cap = match cap {
47 ComponentCapability::Config(c) => c,
48 c => {
49 return Err(RoutingError::unsupported_capability_type(moniker, c.type_name()));
50 }
51 };
52 Ok(Some(cap.value))
53}
54
55pub async fn route_config_value_with_bedrock<C>(
56 use_config: &cm_rust::UseConfigurationDecl,
57 component: &Arc<C>,
58) -> Result<Option<cm_rust::ConfigValue>, router_error::RouterError>
59where
60 C: ComponentInstanceInterface + 'static,
61{
62 let component_sandbox =
63 component.component_sandbox().await.map_err(|e| RoutingError::from(e))?;
64 let capability =
65 match component_sandbox.program_input.config().get_capability(&use_config.target_name) {
66 Some(c) => c,
67 None => {
68 return Err(RoutingError::BedrockNotPresentInDictionary {
69 name: use_config.target_name.to_string(),
70 moniker: component.moniker().clone().into(),
71 }
72 .into());
73 }
74 };
75 let sandbox::Capability::DataRouter(router) = capability else {
76 return Err(RoutingError::BedrockWrongCapabilityType {
77 actual: format!("{:?}", capability),
78 expected: "Router".to_string(),
79 moniker: component.moniker().clone().into(),
80 }
81 .into());
82 };
83 let request =
84 sandbox::Request { metadata: request_metadata::config_metadata(use_config.availability) };
85 let data = match router.route(Some(request), false, component.as_weak().into()).await? {
86 RouterResponse::<Data>::Capability(d) => d,
87 RouterResponse::<Data>::Unavailable => return Ok(use_config.default.clone()),
88 RouterResponse::<Data>::Debug(_) => {
89 return Err(RoutingError::RouteUnexpectedDebug {
90 type_name: cm_rust::CapabilityTypeName::Config,
91 moniker: component.moniker().clone().into(),
92 }
93 .into());
94 }
95 };
96 let sandbox::Data::Bytes(bytes) = data else {
97 return Err(RoutingError::BedrockWrongCapabilityType {
98 actual: format!("{:?}", data),
99 expected: "Data::bytes".to_string(),
100 moniker: component.moniker().clone().into(),
101 }
102 .into());
103 };
104 let config_value: fidl_fuchsia_component_decl::ConfigValue = match fidl::unpersist(&bytes) {
105 Ok(v) => v,
106 Err(_) => {
107 return Err(RoutingError::BedrockWrongCapabilityType {
108 actual: "{unknown}".into(),
109 expected: "fuchsia.component.decl.ConfigValue".into(),
110 moniker: component.moniker().clone().into(),
111 }
112 .into());
113 }
114 };
115
116 Ok(Some(config_value.fidl_into_native()))
117}
118
119pub async fn route_config_value<C>(
124 use_config: &cm_rust::UseConfigurationDecl,
125 component: &Arc<C>,
126) -> Result<Option<cm_rust::ConfigValue>, router_error::RouterError>
127where
128 C: ComponentInstanceInterface + 'static,
129{
130 if let Ok(Some(value)) = route_config_value_with_bedrock(use_config, component).await {
131 return Ok(Some(value));
132 }
133 let source = crate::route_capability(
134 RouteRequest::UseConfig(use_config.clone()),
135 component,
136 &mut crate::mapper::NoopRouteMapper,
137 )
138 .await?;
139 Ok(source_to_value(&use_config.default, source.source)?)
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145 use crate::capability_source::VoidSource;
146 use moniker::Moniker;
147
148 #[test]
149 fn config_from_void() {
150 let void_source = CapabilitySource::Void(VoidSource {
151 capability: crate::capability_source::InternalCapability::Config(
152 "test".parse().unwrap(),
153 ),
154 moniker: Moniker::root(),
155 });
156 assert_eq!(Ok(None), source_to_value(&None, void_source));
157 }
158
159 #[test]
160 fn config_from_capability() {
161 let test_value: cm_rust::ConfigValue = cm_rust::ConfigSingleValue::Uint8(5).into();
162 let void_source = CapabilitySource::Capability(CapabilityToCapabilitySource {
163 source_capability: crate::capability_source::ComponentCapability::Config(
164 cm_rust::ConfigurationDecl {
165 name: "test".parse().unwrap(),
166 value: test_value.clone(),
167 },
168 ),
169 moniker: Moniker::root(),
170 });
171 assert_eq!(Ok(Some(test_value)), source_to_value(&None, void_source));
172 }
173
174 #[test]
175 fn config_from_component() {
176 let test_value: cm_rust::ConfigValue = cm_rust::ConfigSingleValue::Uint8(5).into();
177 let void_source = CapabilitySource::Component(ComponentSource {
178 capability: crate::capability_source::ComponentCapability::Config(
179 cm_rust::ConfigurationDecl {
180 name: "test".parse().unwrap(),
181 value: test_value.clone(),
182 },
183 ),
184 moniker: Moniker::root(),
185 });
186 assert_eq!(Ok(Some(test_value)), source_to_value(&None, void_source));
187 }
188}