blob: c26830b40db4c68eb85b644dd416a15b5e575f8c [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,
cm_rust::CapabilityPath,
directory_broker::DirectoryBroker,
fidl::endpoints::ServerEnd,
fidl_fidl_examples_echo::{EchoMarker, EchoRequest, EchoRequestStream},
fidl_fuchsia_io::{
DirectoryMarker, DirectoryProxy, NodeMarker, CLONE_FLAG_SAME_RIGHTS, MODE_TYPE_DIRECTORY,
OPEN_RIGHT_READABLE, OPEN_RIGHT_WRITABLE,
},
fuchsia_async as fasync,
futures::TryStreamExt,
std::{collections::HashMap, convert::TryFrom, sync::Arc},
vfs::{
self, directory::entry::DirectoryEntry, directory::immutable::simple as pfs,
execution_scope::ExecutionScope, file::pcb::asynchronous::read_only_const,
tree_builder::TreeBuilder,
},
};
/// Used to construct and then host an outgoing directory.
#[derive(Clone)]
pub struct OutDir {
paths: HashMap<CapabilityPath, 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: CapabilityPath, entry: Arc<dyn DirectoryEntry>) {
self.paths.insert(path, entry);
}
/// Adds a file providing the echo service at the given path.
pub fn add_echo_service(&mut self, path: CapabilityPath) {
self.add_entry(path, DirectoryBroker::new(Box::new(Self::echo_server_fn)));
}
/// Adds a static file at the given path.
pub fn add_static_file(&mut self, path: CapabilityPath, contents: &str) {
self.add_entry(path, read_only_const(contents.to_string().into_bytes()));
}
/// Adds the given directory proxy at location "/data".
pub fn add_directory_proxy(&mut self, test_dir_proxy: &DirectoryProxy) {
self.add_entry(
CapabilityPath::try_from("/data").unwrap(),
DirectoryBroker::from_directory_proxy(
io_util::clone_directory(&test_dir_proxy, CLONE_FLAG_SAME_RIGHTS)
.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<DirectoryMarker>| {
dir.clone().open(
ExecutionScope::new(),
OPEN_RIGHT_READABLE | OPEN_RIGHT_WRITABLE,
MODE_TYPE_DIRECTORY,
vfs::path::Path::empty(),
ServerEnd::new(server_end.into_channel()),
);
})
}
/// Hosts a new service on `server_end` that implements `fidl.examples.echo.Echo`.
fn echo_server_fn(
_flags: u32,
_mode: u32,
_relative_path: String,
server_end: ServerEnd<NodeMarker>,
) {
fasync::Task::spawn(async move {
let server_end: ServerEnd<EchoMarker> = ServerEnd::new(server_end.into_channel());
let mut stream: EchoRequestStream = server_end.into_stream().unwrap();
while let Some(EchoRequest::EchoString { value, responder }) =
stream.try_next().await.unwrap()
{
responder.send(value.as_ref().map(|s| &**s)).unwrap();
}
})
.detach();
}
}