blob: f20fa4b6184d9c4dd03e970431000f0026e56326 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
super::mocks::HostFn,
fidl::endpoints::ServerEnd,
fidl_fidl_examples_routing_echo::{EchoRequest, EchoRequestStream},
fidl_fuchsia_io as fio,
futures::TryStreamExt,
std::{collections::HashMap, sync::Arc},
vfs::{
directory::{entry::DirectoryEntry, entry_container::Directory, immutable::simple as pfs},
execution_scope::ExecutionScope,
file::vmo::read_only,
remote::remote_dir,
service::host,
tree_builder::TreeBuilder,
},
};
/// Used to construct and then host an outgoing directory.
#[derive(Clone)]
pub struct OutDir {
paths: HashMap<cm_types::Path, Arc<dyn DirectoryEntry>>,
}
impl OutDir {
pub fn new() -> OutDir {
OutDir { paths: HashMap::new() }
}
/// Add a `DirectoryEntry` served at the given path.
pub fn add_entry(&mut self, path: cm_types::Path, entry: Arc<dyn DirectoryEntry>) {
self.paths.insert(path, entry);
}
/// Adds a file providing the echo protocol at the given path.
pub fn add_echo_protocol(&mut self, path: cm_types::Path) {
self.add_entry(path, host(Self::echo_protocol_fn));
}
/// Adds a static file at the given path.
pub fn add_static_file(&mut self, path: cm_types::Path, contents: &str) {
self.add_entry(path, read_only(String::from(contents)));
}
/// Adds the given directory proxy at location "/data".
pub fn add_directory_proxy(&mut self, test_dir_proxy: &fio::DirectoryProxy) {
self.add_entry(
"/data".parse().unwrap(),
remote_dir(
fuchsia_fs::directory::clone_no_describe(&test_dir_proxy, None)
.expect("could not clone directory"),
),
);
}
/// Build the output directory.
fn build_out_dir(&self) -> Result<Arc<pfs::Simple>, anyhow::Error> {
let mut tree = TreeBuilder::empty_dir();
// Add any external files.
for (path, entry) in self.paths.iter() {
let path = path.split();
let path = path.iter().map(|x| x.as_str()).collect::<Vec<_>>();
tree.add_entry(&path, entry.clone())?;
}
Ok(tree.build())
}
/// Returns a function that will host this outgoing directory on the given ServerEnd.
pub fn host_fn(&self) -> HostFn {
// Build the output directory.
let dir = self.build_out_dir().expect("could not build out directory");
// Construct a function. Each time it is invoked, we connect a new Zircon channel
// `server_end` to the directory.
Box::new(move |server_end: ServerEnd<fio::DirectoryMarker>| {
dir.clone().open(
ExecutionScope::new(),
fio::OpenFlags::RIGHT_READABLE
| fio::OpenFlags::RIGHT_WRITABLE
| fio::OpenFlags::DIRECTORY,
vfs::path::Path::dot(),
ServerEnd::new(server_end.into_channel()),
);
})
}
/// Hosts a new protocol on `server_end` that implements `fidl.examples.routing.echo.Echo`.
pub async fn echo_protocol_fn(mut stream: EchoRequestStream) {
while let Some(EchoRequest::EchoString { value, responder }) =
stream.try_next().await.unwrap()
{
responder.send(value.as_ref().map(|s| &**s)).unwrap();
}
}
}