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