1use crate::CapabilityBound;
6use std::fmt;
7
8#[derive(Clone)]
21pub struct DirEntry {
22 #[cfg(target_os = "fuchsia")]
23 pub(crate) entry: std::sync::Arc<dyn vfs::directory::entry::DirectoryEntry>,
24}
25
26impl CapabilityBound for DirEntry {
27 fn debug_typename() -> &'static str {
28 "DirEntry"
29 }
30}
31
32#[cfg(target_os = "fuchsia")]
33impl fmt::Debug for DirEntry {
34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 f.debug_struct("DirEntry").field("entry_type", &self.entry.entry_info().type_()).finish()
36 }
37}
38
39#[cfg(not(target_os = "fuchsia"))]
40impl fmt::Debug for DirEntry {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 f.debug_struct("DirEntry").finish()
43 }
44}
45
46#[cfg(target_os = "fuchsia")]
47#[cfg(test)]
48mod tests {
49 use crate::fidl::RemotableCapability;
50 use crate::{Capability, Connector, Dict, DirEntry, WeakInstanceToken};
51 use assert_matches::assert_matches;
52 use fidl::endpoints::ClientEnd;
53 use fidl::{AsHandleRef, Channel};
54 use futures::StreamExt;
55 use vfs::ToObjectRequest as _;
56 use vfs::directory::entry::OpenRequest;
57 use vfs::execution_scope::ExecutionScope;
58 use zx::Status;
59 use {fidl_fuchsia_io as fio, fuchsia_async as fasync};
60
61 const FLAGS: fio::Flags = fio::Flags::PROTOCOL_SERVICE;
62
63 #[fuchsia::test]
64 async fn test_connector_into_open() {
65 let (receiver, sender) = Connector::new();
66 let scope = ExecutionScope::new();
67 let dir_entry = DirEntry::new(
68 sender
69 .try_into_directory_entry(scope.clone(), WeakInstanceToken::new_invalid())
70 .unwrap(),
71 );
72 let (client_end, server_end) = Channel::create();
73 FLAGS.to_object_request(server_end).handle(|request| {
74 dir_entry.open_entry(OpenRequest::new(scope, FLAGS, vfs::Path::dot(), request))
75 });
76 let msg = receiver.receive().await.unwrap();
77 assert_eq!(
78 client_end.basic_info().unwrap().related_koid,
79 msg.channel.basic_info().unwrap().koid
80 );
81 }
82
83 #[test]
84 fn test_connector_into_open_extra_path() {
85 let mut ex = fasync::TestExecutor::new();
86
87 let (receiver, sender) = Connector::new();
88 let scope = ExecutionScope::new();
89 let dir_entry = DirEntry::new(
90 sender
91 .try_into_directory_entry(scope.clone(), WeakInstanceToken::new_invalid())
92 .unwrap(),
93 );
94 let (client_end, server_end) = Channel::create();
95 let path = vfs::Path::validate_and_split("foo").unwrap();
96 FLAGS
97 .to_object_request(server_end)
98 .handle(|request| dir_entry.open_entry(OpenRequest::new(scope, FLAGS, path, request)));
99
100 let mut fut = std::pin::pin!(receiver.receive());
101 assert!(ex.run_until_stalled(&mut fut).is_pending());
102
103 let client_end: ClientEnd<fio::NodeMarker> = client_end.into();
104 let node: fio::NodeProxy = client_end.into_proxy();
105 let result = ex.run_singlethreaded(node.take_event_stream().next()).unwrap();
106 assert_matches!(
107 result,
108 Err(fidl::Error::ClientChannelClosed { status, .. })
109 if status == Status::NOT_DIR
110 );
111 }
112
113 #[fuchsia::test]
114 async fn test_connector_into_open_via_dict() {
115 let dict = Dict::new();
116 let (receiver, sender) = Connector::new();
117 dict.insert("echo".parse().unwrap(), Capability::Connector(sender))
118 .expect("dict entry already exists");
119
120 let scope = ExecutionScope::new();
121 let dir_entry = DirEntry::new(
122 dict.try_into_directory_entry(scope.clone(), WeakInstanceToken::new_invalid()).unwrap(),
123 );
124 let (client_end, server_end) = Channel::create();
125 let path = vfs::Path::validate_and_split("echo").unwrap();
126 FLAGS
127 .to_object_request(server_end)
128 .handle(|request| dir_entry.open_entry(OpenRequest::new(scope, FLAGS, path, request)));
129
130 let msg = receiver.receive().await.unwrap();
131 assert_eq!(
132 client_end.basic_info().unwrap().related_koid,
133 msg.channel.basic_info().unwrap().koid
134 );
135 }
136
137 #[test]
138 fn test_connector_into_open_via_dict_extra_path() {
139 let mut ex = fasync::TestExecutor::new();
140
141 let dict = Dict::new();
142 let (receiver, sender) = Connector::new();
143 dict.insert("echo".parse().unwrap(), Capability::Connector(sender))
144 .expect("dict entry already exists");
145
146 let scope = ExecutionScope::new();
147 let dir_entry = DirEntry::new(
148 dict.try_into_directory_entry(scope.clone(), WeakInstanceToken::new_invalid()).unwrap(),
149 );
150 let (client_end, server_end) = Channel::create();
151 let path = vfs::Path::validate_and_split("echo/foo").unwrap();
152 FLAGS
153 .to_object_request(server_end)
154 .handle(|request| dir_entry.open_entry(OpenRequest::new(scope, FLAGS, path, request)));
155
156 let mut fut = std::pin::pin!(receiver.receive());
157 assert!(ex.run_until_stalled(&mut fut).is_pending());
158
159 let client_end: ClientEnd<fio::NodeMarker> = client_end.into();
160 let node: fio::NodeProxy = client_end.into_proxy();
161 let result = ex.run_singlethreaded(node.take_event_stream().next()).unwrap();
162 assert_matches!(
163 result,
164 Err(fidl::Error::ClientChannelClosed { status, .. })
165 if status == Status::NOT_DIR
166 );
167 }
168}