blob: 3546f064f6d6f770d694e1c8e854228572a5cb80 [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::collection::AssetCollectionError,
anyhow::{format_err, Error},
async_trait::async_trait,
fidl::endpoints::create_proxy,
fidl_fuchsia_io as io, fidl_fuchsia_mem as mem,
fidl_fuchsia_pkg::{FontResolverMarker, UpdatePolicy},
fuchsia_component::client::connect_to_service,
fuchsia_trace as trace, fuchsia_zircon as zx,
manifest::v2,
std::{fs::File, path::Path},
};
/// A trait that covers the interactions of the font service with `fuchsia.pkg.FontResolver` and
/// font asset VMOs. Intended for easier testing.
#[async_trait]
pub trait AssetLoader {
/// Resolves a font package to its `Directory`.
async fn fetch_package_directory(
&self,
package_locator: &v2::PackageLocator,
) -> Result<io::DirectoryProxy, AssetCollectionError>;
/// Gets a `VMO` handle to the [`Asset`] at a local `path`.
fn load_vmo_from_path(&self, path: &Path) -> Result<mem::Buffer, AssetCollectionError>;
/// Gets a VMO handle to the file with the given `file_name` within the given directory.
/// The `package_locator` is used for detailed error messages.
async fn load_vmo_from_directory_proxy(
&self,
directory_proxy: io::DirectoryProxy,
package_locator: &v2::PackageLocator,
file_name: &str,
) -> Result<mem::Buffer, AssetCollectionError>;
}
/// Real implementation of [`AssetLoader`].
pub struct AssetLoaderImpl {}
impl AssetLoaderImpl {
/// Creates a new instance of `AssetLoaderImpl`.
pub fn new() -> Self {
AssetLoaderImpl {}
}
}
/// This implementation is currently covered by integration tests only.
/// TODO(fxbug.dev/48649): Unit tests.
#[async_trait]
impl AssetLoader for AssetLoaderImpl {
async fn fetch_package_directory(
&self,
package_locator: &v2::PackageLocator,
) -> Result<io::DirectoryProxy, AssetCollectionError> {
let package_url = package_locator.url.to_string();
trace::duration!(
"fonts",
"asset:fetcher:fetch_package_directory",
"package_url" => &package_url[..]);
// Get directory handle from FontResolver
let font_resolver = connect_to_service::<FontResolverMarker>()
.map_err(|e| AssetCollectionError::ServiceConnectionError(e.into()))?;
let mut update_policy = UpdatePolicy { fetch_if_absent: true, allow_old_versions: false };
let (dir_proxy, dir_request) = create_proxy::<io::DirectoryMarker>()
.map_err(|e| AssetCollectionError::ServiceConnectionError(e.into()))?;
let response =
font_resolver.resolve(&package_url, &mut update_policy, dir_request).await.map_err(
|e| AssetCollectionError::PackageResolverError(package_locator.clone(), e.into()),
)?;
let () = response.map_err(|i| {
AssetCollectionError::PackageResolverError(
package_locator.clone(),
zx::Status::from_raw(i).into(),
)
})?;
Ok(dir_proxy)
}
fn load_vmo_from_path(&self, path: &Path) -> Result<mem::Buffer, AssetCollectionError> {
let path_string = path.to_str().unwrap_or_default();
trace::duration!(
"fonts",
"asset:fetcher:load_vmo_from_path",
"path" => path_string);
let file = File::open(path)
.map_err(|e| AssetCollectionError::LocalFileNotAccessible(path.to_owned(), e.into()))?;
let vmo = fdio::get_vmo_copy_from_file(&file)
.map_err(|e| AssetCollectionError::LocalFileNotAccessible(path.to_owned(), e.into()))?;
let size = file
.metadata()
.map_err(|e| AssetCollectionError::LocalFileNotAccessible(path.to_owned(), e.into()))?
.len();
Ok(mem::Buffer { vmo, size })
}
async fn load_vmo_from_directory_proxy(
&self,
directory_proxy: io::DirectoryProxy,
package_locator: &v2::PackageLocator,
file_name: &str,
) -> Result<mem::Buffer, AssetCollectionError> {
trace::duration!(
"fonts",
"asset:collection:load_buffer_from_directory_proxy",
"file_name" => file_name);
let packaged_file_error = |cause: Error| AssetCollectionError::PackagedFileError {
file_name: file_name.to_string(),
package_locator: package_locator.clone(),
cause,
};
let file_proxy =
io_util::open_file(&directory_proxy, Path::new(&file_name), io::OPEN_RIGHT_READABLE)
.map_err(|e| packaged_file_error(e))?;
let (status, buffer) = file_proxy
.get_buffer(io::VMO_FLAG_READ)
.await
.map_err(|e| packaged_file_error(e.into()))?;
zx::Status::ok(status).map_err(|e| packaged_file_error(e.into()))?;
buffer.map(|b| *b).ok_or_else(|| {
packaged_file_error(
format_err!(
"Inexplicably failed to access buffer after opening the file successfully"
)
.into(),
)
})
}
}