1use fidl::endpoints::{create_proxy, ProtocolMarker};
9use moniker::Moniker;
10use thiserror::Error;
11use {fidl_fuchsia_io as fio, fidl_fuchsia_sys2 as fsys};
12
13#[derive(Debug, Error)]
15pub enum OpenError {
16 #[error("instance {0} could not be found")]
17 InstanceNotFound(Moniker),
18 #[error("opening {0} requires {1} to be resolved")]
19 InstanceNotResolved(OpenDirType, Moniker),
20 #[error("opening {0} requires {1} to be running")]
21 InstanceNotRunning(OpenDirType, Moniker),
22 #[error("component manager's open request on the directory returned a FIDL error")]
23 OpenFidlError,
24 #[error("{0} does not have a {1}")]
25 NoSuchDir(Moniker, OpenDirType),
26 #[error("component manager could not parse moniker: {0}")]
27 BadMoniker(Moniker),
28 #[error("component manager could not parse dir type: {0}")]
29 BadDirType(OpenDirType),
30 #[error("component manager could not parse path: {0}")]
31 BadPath(String),
32 #[error("component manager responded with an unknown error code")]
33 UnknownError,
34 #[error(transparent)]
35 Fidl(#[from] fidl::Error),
36}
37
38#[derive(Clone, Debug)]
40pub enum OpenDirType {
41 Outgoing,
43 Runtime,
45 Package,
47 Exposed,
49 Namespace,
51}
52
53impl std::fmt::Display for OpenDirType {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 match self {
56 Self::Outgoing => write!(f, "outgoing directory"),
57 Self::Runtime => write!(f, "runtime directory"),
58 Self::Package => write!(f, "package directory"),
59 Self::Exposed => write!(f, "exposed directory"),
60 Self::Namespace => write!(f, "namespace directory"),
61 }
62 }
63}
64
65impl Into<fsys::OpenDirType> for OpenDirType {
66 fn into(self) -> fsys::OpenDirType {
67 match self {
68 Self::Outgoing => fsys::OpenDirType::OutgoingDir,
69 Self::Runtime => fsys::OpenDirType::RuntimeDir,
70 Self::Package => fsys::OpenDirType::PackageDir,
71 Self::Exposed => fsys::OpenDirType::ExposedDir,
72 Self::Namespace => fsys::OpenDirType::NamespaceDir,
73 }
74 }
75}
76
77impl From<fsys::OpenDirType> for OpenDirType {
78 fn from(fidl_type: fsys::OpenDirType) -> Self {
79 match fidl_type {
80 fsys::OpenDirType::OutgoingDir => Self::Outgoing,
81 fsys::OpenDirType::RuntimeDir => Self::Runtime,
82 fsys::OpenDirType::PackageDir => Self::Package,
83 fsys::OpenDirType::ExposedDir => Self::Exposed,
84 fsys::OpenDirType::NamespaceDir => Self::Namespace,
85 fsys::OpenDirTypeUnknown!() => panic!("This should not be constructed"),
86 }
87 }
88}
89
90pub async fn connect_to_instance_protocol_at_dir_root<P: ProtocolMarker>(
92 moniker: &Moniker,
93 dir_type: OpenDirType,
94 realm: &fsys::RealmQueryProxy,
95) -> Result<P::Proxy, OpenError> {
96 connect_to_instance_protocol_at_path::<P>(moniker, dir_type, P::DEBUG_NAME, realm).await
97}
98
99pub async fn connect_to_instance_protocol_at_path<P: ProtocolMarker>(
101 moniker: &Moniker,
102 dir_type: OpenDirType,
103 path: &str,
104 realm: &fsys::RealmQueryProxy,
105) -> Result<P::Proxy, OpenError> {
106 let (proxy, server_end) = create_proxy::<P>();
107 let server_end = server_end.into_channel();
108 open_in_instance_dir(
109 &moniker,
110 dir_type,
111 fio::OpenFlags::empty(),
112 fio::ModeType::empty(),
113 path,
114 server_end,
115 realm,
116 )
117 .await?;
118 Ok(proxy)
119}
120
121pub async fn open_instance_dir_root_readable(
123 moniker: &Moniker,
124 dir_type: OpenDirType,
125 realm: &fsys::RealmQueryProxy,
126) -> Result<fio::DirectoryProxy, OpenError> {
127 open_instance_subdir_readable(moniker, dir_type, ".", realm).await
128}
129
130pub async fn open_instance_subdir_readable(
132 moniker: &Moniker,
133 dir_type: OpenDirType,
134 path: &str,
135 realm: &fsys::RealmQueryProxy,
136) -> Result<fio::DirectoryProxy, OpenError> {
137 let (root_dir, server_end) = create_proxy::<fio::DirectoryMarker>();
138 let server_end = server_end.into_channel();
139 open_in_instance_dir(
140 moniker,
141 dir_type,
142 fio::OpenFlags::RIGHT_READABLE,
143 fio::ModeType::empty(),
144 path,
145 server_end,
146 realm,
147 )
148 .await?;
149 Ok(root_dir)
150}
151
152pub async fn open_in_instance_dir(
156 moniker: &Moniker,
157 dir_type: OpenDirType,
158 flags: fio::OpenFlags,
159 mode: fio::ModeType,
160 path: &str,
161 object: fidl::Channel,
162 realm: &fsys::RealmQueryProxy,
163) -> Result<(), OpenError> {
164 let moniker_str = moniker.to_string();
165 realm
166 .deprecated_open(&moniker_str, dir_type.clone().into(), flags, mode, path, object.into())
167 .await?
168 .map_err(|e| match e {
169 fsys::OpenError::InstanceNotFound => OpenError::InstanceNotFound(moniker.clone()),
170 fsys::OpenError::InstanceNotResolved => {
171 OpenError::InstanceNotResolved(dir_type, moniker.clone())
172 }
173 fsys::OpenError::InstanceNotRunning => {
174 OpenError::InstanceNotRunning(dir_type, moniker.clone())
175 }
176 fsys::OpenError::NoSuchDir => OpenError::NoSuchDir(moniker.clone(), dir_type),
177 fsys::OpenError::BadDirType => OpenError::BadDirType(dir_type),
178 fsys::OpenError::BadPath => OpenError::BadPath(path.to_string()),
179 fsys::OpenError::BadMoniker => OpenError::BadMoniker(moniker.clone()),
180 fsys::OpenError::FidlError => OpenError::OpenFidlError,
181 _ => OpenError::UnknownError,
182 })
183}
184
185#[cfg(test)]
186mod tests {
187 use fidl::endpoints::spawn_stream_handler;
188 use moniker::Moniker;
189 use {fidl_fuchsia_io as fio, fidl_fuchsia_sys2 as fsys};
190
191 use super::{
192 connect_to_instance_protocol_at_path, open_instance_dir_root_readable,
193 open_instance_subdir_readable, OpenDirType,
194 };
195
196 #[fuchsia::test]
197 async fn test_connect_to_instance_protocol_at_path() {
198 let realm = spawn_stream_handler(move |realm_request| async move {
200 match realm_request {
201 fsys::RealmQueryRequest::DeprecatedOpen {
202 moniker,
203 dir_type,
204 flags,
205 mode: _,
206 path,
207 object: _,
208 responder,
209 } => {
210 assert_eq!(moniker, "moniker");
211 assert_eq!(dir_type, fsys::OpenDirType::NamespaceDir);
212 assert_eq!(flags, fio::OpenFlags::empty());
213 assert_eq!(path, "/path");
214 responder.send(Ok(())).unwrap();
215 }
216 _ => unreachable!(),
217 };
218 });
219
220 connect_to_instance_protocol_at_path::<fsys::RealmQueryMarker>(
221 &Moniker::parse_str("moniker").unwrap(),
222 OpenDirType::Namespace,
223 "/path",
224 &realm,
225 )
226 .await
227 .unwrap();
228 }
229
230 #[fuchsia::test]
231 async fn test_open_instance_subdir_readable() {
232 let realm = spawn_stream_handler(move |realm_request| async move {
234 match realm_request {
235 fsys::RealmQueryRequest::DeprecatedOpen {
236 moniker,
237 dir_type,
238 flags,
239 mode: _,
240 path,
241 object: _,
242 responder,
243 } => {
244 assert_eq!(moniker, "moniker");
245 assert_eq!(dir_type, fsys::OpenDirType::NamespaceDir);
246 assert_eq!(flags, fio::OpenFlags::RIGHT_READABLE);
247 assert_eq!(path, ".");
248 responder.send(Ok(())).unwrap();
249 }
250 _ => unreachable!(),
251 };
252 });
253
254 open_instance_subdir_readable(
255 &Moniker::parse_str("moniker").unwrap(),
256 OpenDirType::Namespace,
257 ".",
258 &realm,
259 )
260 .await
261 .unwrap();
262
263 open_instance_dir_root_readable(
264 &Moniker::parse_str("moniker").unwrap(),
265 OpenDirType::Namespace,
266 &realm,
267 )
268 .await
269 .unwrap();
270 }
271}