fuchsia_pkg/
package_directory.rs1use crate::{
8 MetaContents, MetaContentsError, MetaPackage, MetaPackageError, MetaSubpackages,
9 MetaSubpackagesError,
10};
11use fidl::endpoints::ServerEnd;
12use fuchsia_hash::{Hash, ParseHashError};
13use thiserror::Error;
14use version_history::AbiRevision;
15use {fidl_fuchsia_io as fio, zx_status};
16
17pub use fuchsia_fs::file::ReadError;
19pub use fuchsia_fs::node::{CloneError, CloseError, OpenError};
20
21#[derive(Debug, Error)]
23#[allow(missing_docs)]
24pub enum ReadHashError {
25 #[error("while reading 'meta/'")]
26 Read(#[source] ReadError),
27
28 #[error("while parsing 'meta/'")]
29 Parse(#[source] ParseHashError),
30}
31
32#[derive(Debug, Error)]
34#[allow(missing_docs)]
35pub enum LoadMetaPackageError {
36 #[error("while reading 'meta/package'")]
37 Read(#[source] ReadError),
38
39 #[error("while parsing 'meta/package'")]
40 Parse(#[source] MetaPackageError),
41}
42
43#[derive(Debug, Error)]
46#[allow(missing_docs)]
47pub enum LoadMetaSubpackagesError {
48 #[error("while reading '{}'", MetaSubpackages::PATH)]
49 Read(#[source] ReadError),
50
51 #[error("while parsing '{}'", MetaSubpackages::PATH)]
52 Parse(#[source] MetaSubpackagesError),
53}
54
55#[derive(Debug, Error)]
57#[allow(missing_docs)]
58pub enum LoadMetaContentsError {
59 #[error("while reading 'meta/contents'")]
60 Read(#[source] ReadError),
61
62 #[error("while parsing 'meta/contents'")]
63 Parse(#[source] MetaContentsError),
64}
65
66#[derive(Debug, Error)]
68#[allow(missing_docs)]
69pub enum LoadAbiRevisionError {
70 #[error("while opening '{}'", AbiRevision::PATH)]
71 Open(#[from] OpenError),
72
73 #[error("while reading '{}'", AbiRevision::PATH)]
74 Read(#[from] ReadError),
75
76 #[error("while parsing '{}'", AbiRevision::PATH)]
77 Parse(#[from] std::array::TryFromSliceError),
78}
79
80#[derive(Debug, Clone)]
82pub struct PackageDirectory {
83 proxy: fio::DirectoryProxy,
84}
85
86impl PackageDirectory {
87 pub fn from_proxy(proxy: fio::DirectoryProxy) -> Self {
89 Self { proxy }
90 }
91
92 pub fn create_request() -> Result<(Self, ServerEnd<fio::DirectoryMarker>), fidl::Error> {
95 let (proxy, request) = fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
96 Ok((Self::from_proxy(proxy), request))
97 }
98
99 #[cfg(target_os = "fuchsia")]
101 pub fn open_from_namespace() -> Result<Self, OpenError> {
102 let dir = fuchsia_fs::directory::open_in_namespace("/pkg", fio::PERM_READABLE)?;
103 Ok(Self::from_proxy(dir))
104 }
105
106 pub async fn close(self) -> Result<(), CloseError> {
108 fuchsia_fs::directory::close(self.proxy).await
109 }
110
111 pub fn reopen(&self, dir_request: ServerEnd<fio::DirectoryMarker>) -> Result<(), CloneError> {
113 fuchsia_fs::directory::clone_onto(&self.proxy, dir_request)
114 }
115
116 pub fn into_proxy(self) -> fio::DirectoryProxy {
118 self.proxy
119 }
120
121 async fn read_file_to_string(&self, path: &str) -> Result<String, ReadError> {
124 fuchsia_fs::directory::read_file_to_string(&self.proxy, path).await
125 }
126
127 pub async fn read_file(&self, path: &str) -> Result<Vec<u8>, ReadError> {
129 fuchsia_fs::directory::read_file(&self.proxy, path).await
130 }
131
132 pub async fn merkle_root(&self) -> Result<Hash, ReadHashError> {
134 let merkle = self.read_file_to_string("meta").await.map_err(ReadHashError::Read)?;
135 merkle.parse().map_err(ReadHashError::Parse)
136 }
137
138 pub async fn meta_contents(&self) -> Result<MetaContents, LoadMetaContentsError> {
140 let meta_contents =
141 self.read_file("meta/contents").await.map_err(LoadMetaContentsError::Read)?;
142 let meta_contents = MetaContents::deserialize(meta_contents.as_slice())
143 .map_err(LoadMetaContentsError::Parse)?;
144 Ok(meta_contents)
145 }
146
147 pub async fn meta_package(&self) -> Result<MetaPackage, LoadMetaPackageError> {
149 let meta_package =
150 self.read_file("meta/package").await.map_err(LoadMetaPackageError::Read)?;
151 let meta_package = MetaPackage::deserialize(meta_package.as_slice())
152 .map_err(LoadMetaPackageError::Parse)?;
153 Ok(meta_package)
154 }
155
156 pub async fn meta_subpackages(&self) -> Result<MetaSubpackages, LoadMetaSubpackagesError> {
159 match self.read_file(MetaSubpackages::PATH).await {
160 Ok(file) => Ok(MetaSubpackages::deserialize(file.as_slice())
161 .map_err(LoadMetaSubpackagesError::Parse)?),
162 Err(ReadError::Open(OpenError::OpenError(zx_status::Status::NOT_FOUND))) => {
163 Ok(MetaSubpackages::default())
164 }
165 Err(err) => Err(LoadMetaSubpackagesError::Read(err)),
166 }
167 }
168
169 pub async fn abi_revision(&self) -> Result<AbiRevision, LoadAbiRevisionError> {
171 let abi_revision_bytes = self.read_file(AbiRevision::PATH).await?;
172 Ok(AbiRevision::try_from(abi_revision_bytes.as_slice())?)
173 }
174
175 pub async fn blobs(&self) -> Result<impl Iterator<Item = Hash>, LoadMetaContentsError> {
178 Ok(self.meta_contents().await?.into_hashes_undeduplicated())
179 }
180}
181
182#[cfg(test)]
183#[cfg(target_os = "fuchsia")]
184mod tests {
185 use super::*;
186 use assert_matches::assert_matches;
187 use fidl::endpoints::Proxy;
188
189 #[fuchsia_async::run_singlethreaded(test)]
190 async fn open_close() {
191 let pkg = PackageDirectory::open_from_namespace().unwrap();
192 let () = pkg.close().await.unwrap();
193 }
194
195 #[fuchsia_async::run_singlethreaded(test)]
196 async fn reopen_is_new_connection() {
197 let pkg = PackageDirectory::open_from_namespace().unwrap();
198
199 let (proxy, server_end) = fidl::endpoints::create_proxy();
200 assert_matches!(pkg.reopen(server_end), Ok(()));
201 assert_matches!(PackageDirectory::from_proxy(proxy).close().await, Ok(()));
202
203 pkg.into_proxy().into_channel().expect("no other users of the wrapped channel");
204 }
205
206 #[fuchsia_async::run_singlethreaded(test)]
207 async fn merkle_root_is_pkg_meta() {
208 let pkg = PackageDirectory::open_from_namespace().unwrap();
209
210 let merkle: Hash = std::fs::read_to_string("/pkg/meta").unwrap().parse().unwrap();
211
212 assert_eq!(pkg.merkle_root().await.unwrap(), merkle);
213 }
214
215 #[fuchsia_async::run_singlethreaded(test)]
216 async fn list_blobs() {
217 let pkg = PackageDirectory::open_from_namespace().unwrap();
218
219 let blobs = pkg.blobs().await.unwrap().collect::<Vec<_>>();
221 assert!(!blobs.is_empty());
222
223 let duplicate_blob_merkle = fuchsia_merkle::from_slice("Hello World!".as_bytes()).root();
225 assert_eq!(blobs.iter().filter(|hash| *hash == &duplicate_blob_merkle).count(), 2);
226 }
227
228 #[fuchsia_async::run_singlethreaded(test)]
229 async fn package_name_is_test_package_name() {
230 let pkg = PackageDirectory::open_from_namespace().unwrap();
231
232 assert_eq!(
233 pkg.meta_package().await.unwrap().into_path().to_string(),
234 "fuchsia-pkg-tests/0"
235 );
236 }
237
238 #[fuchsia_async::run_singlethreaded(test)]
239 async fn missing_subpackages_file_is_empty_subpackages() {
240 let pkg = PackageDirectory::open_from_namespace().unwrap();
241
242 assert_eq!(pkg.meta_subpackages().await.unwrap(), MetaSubpackages::default(),);
243 }
244
245 #[fuchsia_async::run_singlethreaded(test)]
246 async fn abi_revision_succeeds() {
247 let pkg = PackageDirectory::open_from_namespace().unwrap();
248
249 let _: AbiRevision = pkg.abi_revision().await.unwrap();
250 }
251}