blob: 7e05d6f194a0c7a635785e2825c265119783d897 [file] [log] [blame]
// Copyright 2024 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 {fidl_fuchsia_dash as fdash, fidl_fuchsia_io as fio, fidl_fuchsia_pkg as fpkg, tracing::warn};
pub(crate) struct PackageResolver {
resolver: fpkg::PackageResolverProxy,
}
impl PackageResolver {
/// Creates a `PackageResolver`.
/// `fuchsia_pkg_resolver` is the resolver backend to use when resolving "fuchsia-pkg" package
/// URLs.
pub(crate) fn new(
fuchsia_pkg_resolver: fdash::FuchsiaPkgResolver,
) -> Result<Self, fdash::LauncherError> {
// TODO(https://fxbug.dev/329167311) Support bootfs packages
let suffix = match fuchsia_pkg_resolver {
fdash::FuchsiaPkgResolver::Base => "base",
fdash::FuchsiaPkgResolver::Full => "full",
fdash::FuchsiaPkgResolverUnknown!() => {
warn!("unknown fuchsia-pkg resolver: {}", fuchsia_pkg_resolver.into_primitive());
return Err(fdash::LauncherError::PackageResolver);
}
};
let resolver = fuchsia_component::client::connect_to_protocol_at_path::<
fpkg::PackageResolverMarker,
>(&format!("/svc/fuchsia.pkg.PackageResolver-{suffix}"))
.map_err(|_| fdash::LauncherError::PackageResolver)?;
Ok(Self { resolver })
}
/// Resolves `url` and returns a proxy to the package directory.
pub(crate) async fn resolve(&self, url: &str) -> Result<fio::DirectoryProxy, Error> {
self.resolve_subpackage(url, &[]).await
}
/// Resolves `url` and the chain of `subpackages`, if any, and returns a proxy to the final
/// package directory.
pub(crate) async fn resolve_subpackage(
&self,
url: &str,
subpackages: &[String],
) -> Result<fio::DirectoryProxy, Error> {
let (mut dir, server) = fidl::endpoints::create_proxy::<fio::DirectoryMarker>()?;
let mut context = self.resolver.resolve(url, server).await?.map_err(Error::Application)?;
for subpackage in subpackages {
let (sub_dir, server) = fidl::endpoints::create_proxy::<fio::DirectoryMarker>()?;
context = self
.resolver
.resolve_with_context(subpackage, &context, server)
.await?
.map_err(Error::Application)?;
dir = sub_dir;
}
Ok(dir)
}
#[cfg(test)]
pub(crate) fn new_test(resolver: fpkg::PackageResolverProxy) -> Self {
Self { resolver }
}
}
#[derive(thiserror::Error, Debug)]
pub(crate) enum Error {
#[error("fuchsia.pkg/PackageResolver fidl error")]
Fidl(#[from] fidl::Error),
#[error("fuchsia.pkg/PackageResolver application error: {0:?}")]
Application(fpkg::ResolveError),
}
impl Error {
pub(crate) fn while_resolving_tool_package(self) -> fdash::LauncherError {
fdash::LauncherError::PackageResolver
}
pub(crate) fn while_resolving_package_to_explore(self) -> fdash::LauncherError {
match self {
Self::Application(fpkg::ResolveError::PackageNotFound) => {
fdash::LauncherError::ResolveTargetPackage
}
_ => fdash::LauncherError::PackageResolver,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use fuchsia_async as fasync;
use futures::stream::StreamExt as _;
#[fuchsia::test]
async fn chain_subpackage_resolves() {
let (resolver, mut stream) =
fidl::endpoints::create_proxy_and_stream::<fpkg::PackageResolverMarker>().unwrap();
let resolver = crate::package_resolver::PackageResolver::new_test(resolver);
// A mock package resolver that records all requests in `requests`.
let requests = std::rc::Rc::new(std::cell::RefCell::new(vec![]));
let requests_clone = requests.clone();
fasync::Task::local(async move {
while let Some(Ok(request)) = stream.next().await {
match request {
fpkg::PackageResolverRequest::Resolve { package_url, dir: _, responder } => {
requests_clone.borrow_mut().push((package_url.clone(), None));
responder
.send(Ok(&fidl_fuchsia_pkg::ResolutionContext {
bytes: package_url.as_bytes().to_vec(),
}))
.unwrap();
}
fpkg::PackageResolverRequest::ResolveWithContext {
package_url,
context,
dir: _,
responder,
} => {
requests_clone.borrow_mut().push((package_url.clone(), Some(context)));
responder
.send(Ok(&fidl_fuchsia_pkg::ResolutionContext {
bytes: package_url.as_bytes().to_vec(),
}))
.unwrap();
}
req => panic!("unexpected request {req:?}"),
}
}
})
.detach();
let _: fio::DirectoryProxy =
resolver.resolve_subpackage("full-url", &["a".into(), "b".into()]).await.unwrap();
assert_eq!(
*requests.borrow_mut(),
vec![
("full-url".into(), None),
("a".into(), Some(fpkg::ResolutionContext { bytes: "full-url".into() })),
("b".into(), Some(fpkg::ResolutionContext { bytes: "a".into() })),
]
);
}
}